├── .gitignore ├── .travis.yml ├── .travis ├── deploy_packagecloud.sh ├── deploy_production_s3_gpg_private_key.asc.enc ├── deploy_s3.sh ├── deploy_s3_dependencies.sh ├── deploy_staging_s3_gpg_private_key.asc.enc ├── gh-5112-update-repo-sh-use-right-gpg-key.patch ├── gh-5113-update-repo-sh-add-fedora-25-26.patch ├── gh-5114-update-repo-sh-fix-unbound-var-access.patch └── packagecloud-list-repos.patch ├── AUTHORS ├── LICENSE ├── README.PACK.md ├── README.md ├── config.m4 ├── debian ├── changelog ├── compat ├── control.in ├── copyright ├── php-tarantool.postinst.in ├── php-tarantool.postrm.in ├── prebuild.sh ├── rules └── source │ ├── format │ └── options ├── lib ├── __init__.py └── tarantool_server.py ├── package.xml ├── php_tarantool.h ├── rpm └── php-tarantool.spec ├── src ├── php_tarantool.h ├── tarantool.c ├── tarantool_exception.c ├── tarantool_exception.h ├── tarantool_internal.h ├── tarantool_msgpack.c ├── tarantool_msgpack.h ├── tarantool_network.c ├── tarantool_network.h ├── tarantool_proto.c ├── tarantool_proto.h ├── tarantool_schema.c ├── tarantool_schema.h ├── tarantool_tp.c ├── tarantool_tp.h ├── third_party │ ├── PMurHash.c │ ├── PMurHash.h │ ├── mhash.h │ ├── msgpuck.c │ ├── msgpuck.h │ ├── sha1.c │ ├── sha1.h │ └── tp.h ├── utils.c └── utils.h ├── test-run.py ├── test.all.sh ├── test.pkg.all.sh ├── test.pkg.sh ├── test.sh └── test ├── AssertTest.php ├── CreateTest.php ├── DMLTest.php ├── MockTest.php ├── MsgPackTest.php ├── PhpUnitCompat.php ├── RandomTest.php ├── TestHelpers.php ├── bootstrap.php └── shared ├── box.lua ├── phpunit.xml ├── queue.yml ├── tarantool-1.ini ├── tarantool-2.ini ├── tarantool-3.ini └── valgrind.sup /.gitignore: -------------------------------------------------------------------------------- 1 | # Suppress phpize and autotools autogenerated files 2 | 3 | Makefile.in 4 | /autom4te.cache 5 | /aclocal.m4 6 | /compile 7 | /configure 8 | /configure.ac 9 | /depcomp 10 | /install-sh 11 | /missing 12 | /.deps 13 | /Makefile 14 | /Makefile.fragments 15 | /Makefile.global 16 | /Makefile.objects 17 | /acinclude.m4 18 | /config.guess 19 | /config.h 20 | /config.h.in 21 | /config.h.in~ 22 | /config.log 23 | /config.nice 24 | /config.status 25 | /config.sub 26 | /configure.in 27 | /libtool 28 | /ltmain.sh 29 | /mkinstalldirs 30 | /modules/ 31 | /run-tests.php 32 | /tarantool.la 33 | /test/var/ 34 | *.lo 35 | *.loT 36 | *.libs 37 | *.pyc 38 | *.out 39 | *.dSYM 40 | var* 41 | config.guess.cdbs-orig 42 | config.sub.cdbs-orig 43 | debian/files 44 | debian/php5-tarantool.debhelper.log 45 | debian/php5-tarantool.substvars 46 | debian/php5-tarantool/ 47 | build 48 | .*.sw[a-z] 49 | 50 | # Unencrypted private GPG keys for deployment. 51 | .travis/*.asc 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | 4 | language: php 5 | 6 | # Disable tarantool-1.6 and 1.7 on php 7.1-7.4, because 7 | # php-7.[1-4]-cli docker images are based on Debian Buster, but we 8 | # have no tarantool-1.[6-7] packages for this distribution. 9 | jobs: 10 | include: 11 | # PHP 7.0 12 | # ------- 13 | - php: 7.0 14 | env: TARANTOOL_VERSION=1.6 15 | - php: 7.0 16 | env: TARANTOOL_VERSION=1.7 17 | - php: 7.0 18 | env: TARANTOOL_VERSION=1.9 19 | - php: 7.0 20 | env: TARANTOOL_VERSION=1.10 21 | - php: 7.0 22 | env: TARANTOOL_VERSION=2.1 23 | - php: 7.0 24 | env: TARANTOOL_VERSION=2.2 25 | - php: 7.0 26 | env: TARANTOOL_VERSION=2.3 27 | - php: 7.0 28 | env: TARANTOOL_VERSION=2.4 29 | - php: 7.0 30 | env: TARANTOOL_VERSION=2.5 31 | 32 | # PHP 7.1 33 | # ------- 34 | - php: 7.1 35 | env: TARANTOOL_VERSION=1.9 36 | - php: 7.1 37 | env: TARANTOOL_VERSION=1.10 38 | - php: 7.1 39 | env: TARANTOOL_VERSION=2.1 40 | - php: 7.1 41 | env: TARANTOOL_VERSION=2.2 42 | - php: 7.1 43 | env: TARANTOOL_VERSION=2.3 44 | - php: 7.1 45 | env: TARANTOOL_VERSION=2.4 46 | - php: 7.1 47 | env: TARANTOOL_VERSION=2.5 48 | 49 | # PHP 7.2 50 | # ------- 51 | - php: 7.2 52 | env: TARANTOOL_VERSION=1.9 53 | - php: 7.2 54 | env: TARANTOOL_VERSION=1.10 55 | - php: 7.2 56 | env: TARANTOOL_VERSION=2.1 57 | # - php: 7.2 58 | env: TARANTOOL_VERSION=2.2 59 | - php: 7.2 60 | env: TARANTOOL_VERSION=2.3 61 | - php: 7.2 62 | env: TARANTOOL_VERSION=2.4 63 | - php: 7.2 64 | env: TARANTOOL_VERSION=2.5 65 | 66 | # PHP 7.3 67 | # ------- 68 | - php: 7.3 69 | env: TARANTOOL_VERSION=1.9 70 | - php: 7.3 71 | env: TARANTOOL_VERSION=1.10 72 | - php: 7.3 73 | env: TARANTOOL_VERSION=2.1 74 | # - php: 7.3 75 | env: TARANTOOL_VERSION=2.2 76 | - php: 7.3 77 | env: TARANTOOL_VERSION=2.3 78 | - php: 7.3 79 | env: TARANTOOL_VERSION=2.4 80 | - php: 7.3 81 | env: TARANTOOL_VERSION=2.5 82 | 83 | # PHP 7.4 84 | # ------- 85 | - php: 7.4 86 | env: TARANTOOL_VERSION=1.9 87 | - php: 7.4 88 | env: TARANTOOL_VERSION=1.10 89 | - php: 7.4 90 | env: TARANTOOL_VERSION=2.1 91 | # - php: 7.4 92 | env: TARANTOOL_VERSION=2.2 93 | - php: 7.4 94 | env: TARANTOOL_VERSION=2.3 95 | - php: 7.4 96 | env: TARANTOOL_VERSION=2.4 97 | - php: 7.4 98 | env: TARANTOOL_VERSION=2.5 99 | 100 | # RPM packages 101 | # ------------ 102 | # Note: CentOS 6 & 7 do not provide PHP 7.*. 103 | # Note: Fedora < 25 do not provide PHP 7.*. 104 | - env: OS=el DIST=8 105 | - env: OS=fedora DIST=25 106 | - env: OS=fedora DIST=26 107 | - env: OS=fedora DIST=27 108 | - env: OS=fedora DIST=28 109 | - env: OS=fedora DIST=29 110 | - env: OS=fedora DIST=30 111 | - env: OS=fedora DIST=31 112 | 113 | # Deb packages 114 | # ------------ 115 | # Debian < 8 (Stretch) do not provide PHP 7.*. 116 | # Ubuntu < 16.04 (Xenial) do not provide PHP 7.*. 117 | - env: OS=debian DIST=stretch 118 | - env: OS=debian DIST=buster 119 | - env: OS=ubuntu DIST=xenial 120 | - env: OS=ubuntu DIST=bionic 121 | - env: OS=ubuntu DIST=eoan 122 | - env: OS=ubuntu DIST=focal 123 | 124 | python: 125 | - 2.7 126 | 127 | script: 128 | - | 129 | # Make shell strictier. 130 | # 131 | # - Exit with a failure on a first failed command. 132 | # - Print each executed commmand. 133 | set -ex 134 | 135 | if [ -n "${TARANTOOL_VERSION}" ]; then 136 | ./test.sh 137 | elif [ -n "${OS}" ] && [ -n "${DIST}" ]; then 138 | git clone --depth 1 https://github.com/packpack/packpack.git 139 | ./packpack/packpack 140 | if [ "${OS}" = "el" ]; then 141 | DOCKER_IMAGE="centos:${DIST}" 142 | else 143 | DOCKER_IMAGE="${OS}:${DIST}" 144 | fi 145 | docker run \ 146 | --volume "$(realpath .):/tarantool-php" \ 147 | --workdir /tarantool-php \ 148 | --rm \ 149 | "${DOCKER_IMAGE}" \ 150 | ./test.pkg.sh 151 | else 152 | exit 1 153 | fi 154 | 155 | # Deploy 156 | # ------ 157 | 158 | # Skip deployment when it is not expected. 159 | if [ -z "${OS}" ] || [ -z "${DIST}" ]; then 160 | echo "Skip deployment: it is pure testing job w/o any RPM / Deb artefacts" 161 | exit 0 162 | fi 163 | if [ "${TRAVIS_REPO_SLUG}" != "tarantool/tarantool-php" ]; then 164 | echo "Skip deployment: it is a fork, not the base repository" 165 | exit 0 166 | fi 167 | if [ "${TRAVIS_EVENT_TYPE}" != "push" ]; then 168 | echo "Skip deployment: event is not 'push', but ${TRAVIS_EVENT_TYPE}" 169 | exit 0 170 | fi 171 | 172 | # Choose destination to push packages. 173 | if [ "${TRAVIS_BRANCH}" == "master" ] || [ -n "${TRAVIS_TAG}" ]; then 174 | echo "Set production deployment parameters" 175 | configuration=production 176 | else 177 | echo "Set staging deployment parameters" 178 | configuration=staging 179 | fi 180 | 181 | # Deploy to packagecloud repositories. 182 | ./.travis/deploy_packagecloud.sh ${configuration} 183 | 184 | # Deploy to S3 based repositories. 185 | ./.travis/deploy_s3_dependencies.sh 186 | ./.travis/deploy_s3.sh ${configuration} 187 | -------------------------------------------------------------------------------- /.travis/deploy_packagecloud.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Deploy to packagecloud repositories 4 | # ----------------------------------- 5 | # 6 | # `deploy_packagecloud.sh` is equivalent to 7 | # `deploy_packagecloud.sh staging`. 8 | # 9 | # `deploy_packagecloud.sh staging` requires the following 10 | # environment variables: 11 | # 12 | # - OS 13 | # - DIST 14 | # - DEPLOY_STAGING_PACKAGECLOUD_USER 15 | # - DEPLOY_STAGING_PACKAGECLOUD_TOKEN 16 | # 17 | # `deploy_packagecloud.sh production` requires the following 18 | # environment variables: 19 | # 20 | # - OS 21 | # - DIST 22 | # - DEPLOY_PRODUCTION_PACKAGECLOUD_USER 23 | # - DEPLOY_PRODUCTION_PACKAGECLOUD_TOKEN 24 | # 25 | # If one of those variables is not set or empty, then deployment 26 | # will be skipped. 27 | 28 | # Make shell strictier. 29 | # 30 | # - Exit with a failure on a first failed command. 31 | # - Exit with a failure on an attempt to use an unset variable. 32 | # - Print each executed commmand. 33 | # 34 | # Note: The script expects that Travis-CI will filter sensitive 35 | # information (such as a token): 'Display value in build log' 36 | # toogle should be OFF for to keep a value secure. 37 | set -eux 38 | 39 | configuration=${1:-staging} 40 | 41 | # Choose credentials. 42 | if [ ${configuration} = staging ]; then 43 | DEPLOY_PACKAGECLOUD_USER="${DEPLOY_STAGING_PACKAGECLOUD_USER:-}" 44 | DEPLOY_PACKAGECLOUD_TOKEN="${DEPLOY_STAGING_PACKAGECLOUD_TOKEN:-}" 45 | elif [ ${configuration} = production ]; then 46 | DEPLOY_PACKAGECLOUD_USER="${DEPLOY_PRODUCTION_PACKAGECLOUD_USER:-}" 47 | DEPLOY_PACKAGECLOUD_TOKEN="${DEPLOY_PRODUCTION_PACKAGECLOUD_TOKEN:-}" 48 | else 49 | echo "Unknown configuration: ${configuration}" 50 | exit 1 51 | fi 52 | 53 | # Skip deployment if some variables are not set or empty. 54 | if [ -z "${OS:-}" ] || [ -z "${DIST:-}" ] || \ 55 | [ -z "${DEPLOY_PACKAGECLOUD_USER}" ] || \ 56 | [ -z "${DEPLOY_PACKAGECLOUD_TOKEN}" ]; then 57 | echo "Skip deployment: some of necessary environment" 58 | echo "variables are not set or empty" 59 | exit 0 60 | fi 61 | 62 | # Verify that packpack is cloned into the current directory. 63 | packagecloud_tool=./packpack/tools/packagecloud 64 | if [ ! -f "${packagecloud_tool}" ]; then 65 | echo "Could not find ${packagecloud_tool}" 66 | exit 1 67 | fi 68 | 69 | # Staging repository: keep older packages in case of a 70 | # version clash. 71 | # 72 | # It would be better to replace old ones, but there is no 73 | # such option in the packagecloud tool we use. It may be 74 | # important if we'll have some manual or automatic testing 75 | # upward a staging repository. But at least CI will not fail 76 | # because a package is already exists. 77 | push_args="" 78 | if [ "${configuration}" = staging ]; then 79 | push_args="${push_args} --ignore-duplicates" 80 | fi 81 | 82 | # Setup environment variables for the packagecloud tool. 83 | export PACKAGECLOUD_TOKEN="${DEPLOY_PACKAGECLOUD_TOKEN}" 84 | 85 | # FIXME: It would be good to upstream the repos_list command. 86 | (cd packpack/tools && 87 | patch -p1 -i ../../.travis/packagecloud-list-repos.patch) 88 | 89 | # Fetch list of repositories from packagecloud.io. 90 | # 91 | # Set default repositories if the attempt to fetch them fails. 92 | # 93 | # We have tarantool repositories on packagecloud.io up to 94 | # 2_4. The next ones present only in the S3 based storage. 95 | repositories="" 96 | for i in $(seq 1 5); do 97 | repositories="$(${packagecloud_tool} list_repos || true)" 98 | [ -n "${repositories}" ] && break 99 | done 100 | [ -z "${repositories}" ] && repositories="1_6 1_7 1_9 1_10 2x 2_2 2_3 2_4" 101 | 102 | for repo in ${repositories}; do 103 | # FIXME: Enable *.ddeb when packagecloud.io will support it. 104 | for file in build/*.rpm build/*.deb build/*.dsc; do 105 | extension=${file##*.} 106 | 107 | # Skip non-matched globs: say, build/*.rpm on Debian. 108 | basename="$(basename "${file}" ".${extension}")" 109 | [ "${basename}" = "*" ] && continue 110 | 111 | # Push all source files listed in .dsc file together with 112 | # the file. 113 | # 114 | # FIXME: It seems logical to move this logic to the 115 | # packagecloud tool we use. 116 | files="${file}" 117 | if [ "${extension}" = "dsc" ]; then 118 | parse_dsc_file='{ 119 | if ($0 == "Files:") { 120 | FILES_SECTION = 1; 121 | } else if (FILES_SECTION != 0) { 122 | print "build/"$3; 123 | } 124 | }' 125 | files="${files} $(awk "${parse_dsc_file}" "${file}")" 126 | fi 127 | 128 | user=${DEPLOY_PACKAGECLOUD_USER} 129 | 130 | # Retry failed attempts to upload a package. 131 | # 132 | # packagecloud.io sometimes replieds with 502 Bad Gateway 133 | # for attempts to push, so retrying is important here. 134 | # 135 | # FIXME: This way we don't differentiate network errors 136 | # and all other ones. It would be much better to retry 137 | # from inside the packagecloud tool (requests library 138 | # supports it). 139 | for i in $(seq 1 5); do 140 | # FIXME: The tool fetches distributions.json each 141 | # time. It can cache the data somewhere and reuse 142 | # during some time period until expiration. 143 | ${packagecloud_tool} push ${push_args} ${user}/${repo} \ 144 | ${extension} ${OS} ${DIST} ${files} && break 145 | done 146 | done 147 | done 148 | -------------------------------------------------------------------------------- /.travis/deploy_production_s3_gpg_private_key.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tarantool/tarantool-php/1e1f6eb7fc7bb61b5ccefc45d5b9817981582b1a/.travis/deploy_production_s3_gpg_private_key.asc.enc -------------------------------------------------------------------------------- /.travis/deploy_s3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Deploy to S3 based repositories 4 | # ------------------------------- 5 | # 6 | # `deploy_s3.sh` is equivalent to `deploy_s3.sh staging`. 7 | # 8 | # `deploy_s3.sh staging` requires the following environment 9 | # variables: 10 | # 11 | # - OS 12 | # - DIST 13 | # - DEPLOY_STAGING_S3_ENDPOINT_URL="https://..." 14 | # - DEPLOY_STAGING_S3_LIVE_DIR="s3://my_bucket/foo/bar/live" 15 | # - DEPLOY_STAGING_S3_RELEASE_DIR="s3://my_bucket/foo/bar/release" 16 | # - DEPLOY_STAGING_S3_ACCESS_KEY_ID 17 | # - DEPLOY_STAGING_S3_SECRET_ACCESS_KEY 18 | # - DEPLOY_STAGING_S3_GPG_KEY_FILE_KEY (32 bytes in hex) 19 | # - DEPLOY_STAGING_S3_GPG_KEY_FILE_IV (16 bytes in hex) 20 | # 21 | # `deploy_s3.sh production` requires the following environment 22 | # variables: 23 | # 24 | # - OS 25 | # - DIST 26 | # - DEPLOY_PRODUCTION_S3_ENDPOINT_URL="https://..." 27 | # - DEPLOY_PRODUCTION_S3_LIVE_DIR="s3://my_bucket/foo/bar/live" 28 | # - DEPLOY_PRODUCTION_S3_RELEASE_DIR="s3://my_bucket/foo/bar/release" 29 | # - DEPLOY_PRODUCTION_S3_ACCESS_KEY_ID 30 | # - DEPLOY_PRODUCTION_S3_SECRET_ACCESS_KEY 31 | # - DEPLOY_PRODUCTION_S3_GPG_KEY_FILE_KEY (32 bytes in hex) 32 | # - DEPLOY_PRODUCTION_S3_GPG_KEY_FILE_IV (16 bytes in hex) 33 | # 34 | # If one of those variables is not set or empty, then deployment 35 | # will be skipped. 36 | # 37 | # The following optional variable will be used to group related 38 | # packages into one directory within a Debian / Ubuntu repository. 39 | # If not set or empty, the variable will be guessed like it is 40 | # done in packpack: 41 | # 42 | # - PRODUCT 43 | # 44 | # **Production** repository directory and credentials will be used 45 | # to fetch a repository list (1.10, 2.1, ...) to deploy into them. 46 | # The production list will be used as for the production 47 | # configuration as well as for the staging one (!). The following 48 | # optional variables should be set to fetch the list, otherwise 49 | # the default one will be used (1.10, 2.1, 2.2, 2.3, 2.4, 2.5): 50 | # 51 | # - DEPLOY_PRODUCTION_S3_ENDPOINT_URL 52 | # - DEPLOY_PRODUCTION_S3_ACCESS_KEY_ID 53 | # - DEPLOY_PRODUCTION_S3_SECRET_ACCESS_KEY 54 | # - DEPLOY_PRODUCTION_S3_ENDPOINT_URL 55 | # - DEPLOY_PRODUCTION_S3_LIVE_DIR 56 | 57 | # Make shell strictier. 58 | # 59 | # - Exit with a failure on a first failed command. 60 | # - Exit with a failure on an attempt to use an unset variable. 61 | # - Print each executed commmand. 62 | # 63 | # Note: The script expects that Travis-CI will filter sensitive 64 | # information (such as a token): 'Display value in build log' 65 | # toogle should be OFF for to keep a value secure. 66 | set -eux 67 | 68 | configuration=${1:-staging} 69 | 70 | # Choose URLs, directories, keys and so. 71 | if [ ${configuration} = staging ]; then 72 | DEPLOY_S3_ENDPOINT_URL="${DEPLOY_STAGING_S3_ENDPOINT_URL:-}" 73 | DEPLOY_S3_LIVE_DIR="${DEPLOY_STAGING_S3_LIVE_DIR:-}" 74 | DEPLOY_S3_RELEASE_DIR="${DEPLOY_STAGING_S3_RELEASE_DIR:-}" 75 | DEPLOY_S3_ACCESS_KEY_ID="${DEPLOY_STAGING_S3_ACCESS_KEY_ID:-}" 76 | DEPLOY_S3_SECRET_ACCESS_KEY="${DEPLOY_STAGING_S3_SECRET_ACCESS_KEY:-}" 77 | DEPLOY_S3_GPG_KEY_FILE_KEY="${DEPLOY_STAGING_S3_GPG_KEY_FILE_KEY:-}" 78 | DEPLOY_S3_GPG_KEY_FILE_IV="${DEPLOY_STAGING_S3_GPG_KEY_FILE_IV:-}" 79 | elif [ ${configuration} = production ]; then 80 | DEPLOY_S3_ENDPOINT_URL="${DEPLOY_PRODUCTION_S3_ENDPOINT_URL:-}" 81 | DEPLOY_S3_LIVE_DIR="${DEPLOY_PRODUCTION_S3_LIVE_DIR:-}" 82 | DEPLOY_S3_RELEASE_DIR="${DEPLOY_PRODUCTION_S3_RELEASE_DIR:-}" 83 | DEPLOY_S3_ACCESS_KEY_ID="${DEPLOY_PRODUCTION_S3_ACCESS_KEY_ID:-}" 84 | DEPLOY_S3_SECRET_ACCESS_KEY="${DEPLOY_PRODUCTION_S3_SECRET_ACCESS_KEY:-}" 85 | DEPLOY_S3_GPG_KEY_FILE_KEY="${DEPLOY_PRODUCTION_S3_GPG_KEY_FILE_KEY:-}" 86 | DEPLOY_S3_GPG_KEY_FILE_IV="${DEPLOY_PRODUCTION_S3_GPG_KEY_FILE_IV:-}" 87 | else 88 | echo "Unknown configuration: ${configuration}" 89 | exit 1 90 | fi 91 | 92 | # Skip deployment if some variables are not set or empty. 93 | if [ -z "${OS:-}" ] || [ -z "${DIST:-}" ] || \ 94 | [ -z "${DEPLOY_S3_ENDPOINT_URL}" ] || \ 95 | [ -z "${DEPLOY_S3_LIVE_DIR}" ] || \ 96 | [ -z "${DEPLOY_S3_RELEASE_DIR}" ] || \ 97 | [ -z "${DEPLOY_S3_ACCESS_KEY_ID}" ] || \ 98 | [ -z "${DEPLOY_S3_SECRET_ACCESS_KEY}" ] || \ 99 | [ -z "${DEPLOY_S3_GPG_KEY_FILE_KEY}" ] || \ 100 | [ -z "${DEPLOY_S3_GPG_KEY_FILE_IV}" ]; then 101 | echo "Skip deployment: some of necessary environment" 102 | echo "variables are not set or empty" 103 | exit 0 104 | fi 105 | 106 | # Download the tool to deploy to an S3 based repository. 107 | ref=f84cb1aae3144f5677feacf6be31bd4f15e91c2d 108 | base_url="https://raw.githubusercontent.com/tarantool/tarantool/${ref}" 109 | curl -Ssfo update_repo.sh "${base_url}/tools/update_repo.sh" 110 | chmod a+x update_repo.sh 111 | 112 | # FIXME: Upstream the patches. 113 | patch -p1 -i .travis/gh-5112-update-repo-sh-use-right-gpg-key.patch 114 | patch -p1 -i .travis/gh-5113-update-repo-sh-add-fedora-25-26.patch 115 | patch -p1 -i .travis/gh-5114-update-repo-sh-fix-unbound-var-access.patch 116 | 117 | # Decrypt a GPG key. 118 | gpg_key_file=".travis/deploy_${configuration}_s3_gpg_private_key.asc" 119 | openssl aes-256-cbc -K "${DEPLOY_S3_GPG_KEY_FILE_KEY}" \ 120 | -iv "${DEPLOY_S3_GPG_KEY_FILE_IV}" -in "${gpg_key_file}.enc" \ 121 | -out "${gpg_key_file}" -d 122 | 123 | # Import GPG key for signing repository files. 124 | gpg --import --batch "${gpg_key_file}" 125 | 126 | # Extract GPG key id for signing repository files. 127 | # 128 | # This way works for both GnuPG 1 and GnuPG 2. The alternative 129 | # would be using '--import-options show-only', but it is available 130 | # only in GnuPG 2. See https://unix.stackexchange.com/a/468889 131 | mkdir -m 0700 temp-gpg-home 132 | gpg --homedir temp-gpg-home --import --batch "${gpg_key_file}" 133 | export GPG_SIGN_KEY="$(gpg --homedir temp-gpg-home --list-secret-keys \ 134 | --with-colons | grep ^sec: | cut -d: -f5)" 135 | rm -rf temp-gpg-home 136 | 137 | # Use SHA256 hashing algorithm for files signing. 138 | # 139 | # `apt-get update` gives a warning when InRelease file signature 140 | # is calculated with SHA1. We should configure GnuPG (which is 141 | # used by reprepro, which is used by update_repo.sh) to sign using 142 | # SHA265. 143 | # 144 | # https://askubuntu.com/a/819868 145 | mkdir -p ~/.gnupg 146 | echo 'digest-algo sha256' >> ~/.gnupg/gpg.conf 147 | 148 | # Setup arguments that are common for all repositories 149 | # (1.10, 2.1, ...). 150 | update_repo_args="--os=${OS} --distribution=${DIST}" 151 | 152 | # ${PRODUCT} value may affect location of *.deb, *.rpm and related 153 | # files relative to a base repository URL. We can provide it or 154 | # miss: the script will generate correct repository metainfo 155 | # anyway. 156 | # 157 | # However providing meaningful value for this option enables 158 | # grouping of related set of packages into a subdirectory named as 159 | # ${PRODUCT} (only for Deb repositories at moment of writing 160 | # this). 161 | # 162 | # It is enabled here for consistency with locations of other Deb 163 | # packages in our repositories, but in fact it is the internal 164 | # detail, which does not lead to any change in the user 165 | # experience. 166 | 167 | # Guess PRODUCT value in the similar way as packpack does. 168 | # 169 | # Guess from Debian package 170 | [ -z "${PRODUCT:-}" ] && PRODUCT="$(grep Source: debian/control \ 171 | 2>/dev/null | awk '{ print $2; }' || true)" 172 | # Guess from Debian package (control file template) 173 | [ -z "${PRODUCT:-}" ] && PRODUCT="$(grep Source: debian/control.in \ 174 | 2>/dev/null | awk '{ print $2; }' || true)" 175 | # Guess from RPM package 176 | [ -z "${PRODUCT:-}" ] && PRODUCT="$(grep Name: rpm/*.spec \ 177 | 2>/dev/null | awk '{ print $2; }' || true)" 178 | # Guess from git repository name 179 | [ -z "${PRODUCT:-}" ] && PRODUCT="$(git config --get remote.origin.url | \ 180 | sed -e 's/.*\///' -e 's/.git$//' || true)" 181 | # Guess from directory name 182 | [ -z "${PRODUCT:-}" ] && PRODUCT="$(basename "$(pwd)" || true)" 183 | 184 | # Add --product option if there is ${PRODUCT} value. Otherwise it 185 | # will fall back to default 'tarantool'. 186 | if [ -n "${PRODUCT:-}" ]; then 187 | update_repo_args="${update_repo_args} --product=${PRODUCT}" 188 | fi 189 | 190 | # Staging repository: rewrite a package if there is a previous one 191 | # of the same version. 192 | # 193 | # Note: It differs from a logic in deploy_packagecloud.sh. 194 | if [ "${configuration}" = staging ]; then 195 | update_repo_args="${update_repo_args} --force" 196 | fi 197 | 198 | # Fetch actual list of repositories (1.10, 2.1, ...). 199 | # 200 | # Always use production repositories list: even for staging 201 | # deployments. The reason is that it is always actual, while 202 | # we cannot guarantee it for staging repositories. 203 | # 204 | # Note: It differs from a logic in deploy_packagecloud.sh. 205 | repositories="" 206 | if [ -n "${DEPLOY_PRODUCTION_S3_ACCESS_KEY_ID:-}" ] && \ 207 | [ -n "${DEPLOY_PRODUCTION_S3_SECRET_ACCESS_KEY:-}" ] && \ 208 | [ -n "${DEPLOY_PRODUCTION_S3_ENDPOINT_URL:-}" ] && \ 209 | [ -n "${DEPLOY_PRODUCTION_S3_LIVE_DIR:-}" ]; then 210 | export AWS_ACCESS_KEY_ID="${DEPLOY_PRODUCTION_S3_ACCESS_KEY_ID}" 211 | export AWS_SECRET_ACCESS_KEY="${DEPLOY_PRODUCTION_S3_SECRET_ACCESS_KEY}" 212 | 213 | url="${DEPLOY_PRODUCTION_S3_ENDPOINT_URL}" 214 | # Single slash at end matters. 215 | dir="${DEPLOY_PRODUCTION_S3_LIVE_DIR%%/}/" 216 | 217 | repositories="$(aws --endpoint-url "${url}" s3 ls "${dir}" | \ 218 | grep PRE | awk '{ print $2; }' | sed 's@/$@@' || true)" 219 | fi 220 | [ -z "${repositories}" ] && repositories="1.10 2.1 2.2 2.3 2.4 2.5" 221 | 222 | # Setup environment variables for the update_repo.sh tool. 223 | export AWS_S3_ENDPOINT_URL="${DEPLOY_S3_ENDPOINT_URL}" 224 | export AWS_ACCESS_KEY_ID="${DEPLOY_S3_ACCESS_KEY_ID}" 225 | export AWS_SECRET_ACCESS_KEY="${DEPLOY_S3_SECRET_ACCESS_KEY}" 226 | 227 | # Deploy to S3 based repositories. 228 | for repo in ${repositories}; do 229 | # Note: The update_repo.sh tool automatically find 230 | # *.{rpm,deb,dsc} within a passed directory, so we just 231 | # pass the directory name: 'build'. 232 | 233 | # FIXME: Machine-local locking that is used in the 234 | # update_repo.sh tool is insufficient when we deploy from a 235 | # just created virtual machine. 236 | 237 | # Deploy to live repository (per-push). 238 | bucket="${DEPLOY_S3_LIVE_DIR}/${repo}" 239 | ./update_repo.sh ${update_repo_args} --bucket="${bucket}" build 240 | 241 | # Deploy to release repository (tagged commits). 242 | if [ -n "${TRAVIS_TAG:-}" ]; then 243 | bucket="${DEPLOY_S3_RELEASE_DIR}/${repo}" 244 | ./update_repo.sh ${update_repo_args} --bucket="${bucket}" build 245 | fi 246 | done 247 | -------------------------------------------------------------------------------- /.travis/deploy_s3_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Make shell strictier. 4 | # 5 | # - Exit with a failure on a first failed command. 6 | # - Exit with a failure on an attempt to use an unset variable. 7 | # - Print each executed commmand. 8 | set -eux 9 | 10 | # Prevent procmail package from asking configuration parameters 11 | # interactively. 12 | # See https://github.com/packpack/packpack/issues/7 13 | export DEBIAN_FRONTEND=noninteractive 14 | SUDO="sudo -E" 15 | 16 | ${SUDO} apt-get update > /dev/null 17 | 18 | ${SUDO} apt-get install -y pcregrep 19 | ${SUDO} apt-get install -y procmail # for lockfile tool 20 | ${SUDO} apt-get install -y awscli 21 | ${SUDO} apt-get install -y reprepro 22 | ${SUDO} apt-get install -y createrepo 23 | -------------------------------------------------------------------------------- /.travis/deploy_staging_s3_gpg_private_key.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tarantool/tarantool-php/1e1f6eb7fc7bb61b5ccefc45d5b9817981582b1a/.travis/deploy_staging_s3_gpg_private_key.asc.enc -------------------------------------------------------------------------------- /.travis/gh-5112-update-repo-sh-use-right-gpg-key.patch: -------------------------------------------------------------------------------- 1 | --- a/update_repo.sh 2020-06-09 17:35:03.332961335 +0300 2 | +++ b/update_repo.sh 2020-06-23 02:26:55.532653673 +0300 3 | @@ -415,7 +415,7 @@ 4 | done 5 | # resign the selfsigned InRelease file 6 | $rm_file InRelease 7 | - gpg --clearsign -o InRelease Release 8 | + gpg -u $GPG_SIGN_KEY --clearsign -o InRelease Release 9 | # resign the Release file 10 | $rm_file Release.gpg 11 | gpg -u $GPG_SIGN_KEY -abs -o Release.gpg Release 12 | @@ -784,7 +784,7 @@ 13 | EOF 14 | done 15 | tail -n 1 repodata.adding/repomd.xml >>repodata/repomd.xml 16 | - gpg --detach-sign --armor repodata/repomd.xml 17 | + gpg -u $GPG_SIGN_KEY --detach-sign --armor repodata/repomd.xml 18 | 19 | # copy the packages to S3 20 | for file in $pack_rpms ; do 21 | @@ -901,7 +901,7 @@ 22 | tail -n 1 repomd_saved.xml >>repomd.xml 23 | rm -f repomd_saved.xml repomd.xml.asc 24 | popd 25 | - gpg --detach-sign --armor repodata/repomd.xml 26 | + gpg -u $GPG_SIGN_KEY --detach-sign --armor repodata/repomd.xml 27 | 28 | # update the metadata at the S3 29 | $aws_sync_public repodata "$bucket_path/$repopath/repodata" 30 | -------------------------------------------------------------------------------- /.travis/gh-5113-update-repo-sh-add-fedora-25-26.patch: -------------------------------------------------------------------------------- 1 | --- a/update_repo.sh 2020-06-25 16:30:24.309988800 +0300 2 | +++ b/update_repo.sh 2020-06-25 16:29:56.055990528 +0300 3 | @@ -30,7 +30,7 @@ 4 | elif [ "$os" == "el" ]; then 5 | alldists='6 7 8' 6 | elif [ "$os" == "fedora" ]; then 7 | - alldists='27 28 29 30 31' 8 | + alldists='25 26 27 28 29 30 31' 9 | fi 10 | 11 | echo "$alldists" 12 | -------------------------------------------------------------------------------- /.travis/gh-5114-update-repo-sh-fix-unbound-var-access.patch: -------------------------------------------------------------------------------- 1 | --- a/update_repo.sh 2020-06-25 19:15:52.899381514 +0300 2 | +++ b/update_repo.sh 2020-06-25 19:16:29.222379292 +0300 3 | @@ -699,7 +699,7 @@ 4 | for metafile in repodata.base/other \ 5 | repodata.base/filelists \ 6 | repodata.base/primary ; do 7 | - up_lines='' 8 | + up_full_lines='' 9 | if [ "$metafile" == "repodata.base/primary" ]; then 10 | up_full_lines='(\N+\n)*' 11 | fi 12 | @@ -845,7 +845,7 @@ 13 | # entry in damaged file, to fix it all found entries 14 | # of this file need to be removed 15 | for metafile in other filelists primary ; do 16 | - up_lines='' 17 | + up_full_lines='' 18 | if [ "$metafile" == "primary" ]; then 19 | up_full_lines='(\N+\n)*' 20 | fi 21 | -------------------------------------------------------------------------------- /.travis/packagecloud-list-repos.patch: -------------------------------------------------------------------------------- 1 | --- a/packagecloud 2020-06-26 17:34:40.754468234 +0300 2 | +++ b/packagecloud 2020-06-26 17:46:34.527424576 +0300 3 | @@ -93,6 +93,11 @@ 4 | print('prune', pkgpath) 5 | self.apidelete('/api/v1/repos/' + pkgpath) 6 | 7 | + def list_repos(self): 8 | + repos = self.apiget('/api/v1/repos') 9 | + for repo in repos: 10 | + print(repo['name']) 11 | + 12 | 13 | if __name__ == '__main__': 14 | # Initialize argument parser 15 | @@ -132,6 +137,11 @@ 16 | prune_parser.add_argument('--keep', type=int, default=2, 17 | help='The number of package versions to keep.') 18 | 19 | + # Add 'list_repos' command 20 | + list_repos_parser = subparsers.add_parser('list_repos', 21 | + help='list repositories of a user (determined by a token)') 22 | + list_repos_parser.set_defaults(method=PackageCloud.list_repos) 23 | + 24 | args = parser.parse_args() 25 | if not 'method' in args: 26 | parser.print_help() 27 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Tarantool is a collective effort, and incorporates 2 | many contributions from the community. 3 | 4 | Below follows a list of people, who contributed their code. 5 | 6 | Aleksey Demakov, Aleksey Mashanov, Alexandre Kalendarev, 7 | Damien Lefortier, Dmitry E. Oboukhov, Dmitry Simonenko, 8 | Konstantin Osipov, Konstantin Shulgin, Mons Anderson, 9 | Pavel Cherenkov, Roman Antipin, Roman Tokarev, Roman Tsisyk, 10 | Teodor Sigaev, Timofey Khryukin, Yuriy Nevinitsin, Yuriy Vostrikov, 11 | Veniamin Gvozdikov, Aleksandr Lyapunov 12 | 13 | NOTE: If you can commit a change to this list, please do not hesitate 14 | to add your name to it. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010-2013 Tarantool AUTHORS: 2 | please see AUTHORS file. 3 | 4 | /* 5 | * Redistribution and use in source and binary forms, with or 6 | * without modification, are permitted provided that the following 7 | * conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above 10 | * copyright notice, this list of conditions and the 11 | * following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the following 15 | * disclaimer in the documentation and/or other materials 16 | * provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22 | * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 26 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 29 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | -------------------------------------------------------------------------------- /README.PACK.md: -------------------------------------------------------------------------------- 1 | RPM Package 2 | =============================================================================== 3 | 4 | To build an RPM run: 5 | ``` 6 | git archive --format tar ${VERSION} | gzip -9 > ${HOME}/rpmbuild/SOURCES/${VERSION}.tar.gz 7 | cp rpm/tarantool.ini ${HOME}/rpmbuild/SOURCES/ 8 | rpmbuild -bb rpm/php-tarantool.spec 9 | ``` 10 | When the build ends you'll find an RPM in `${HOME}/rpmbuild/(x86_64|i386)/` 11 | Current `${VERSION}` is `0.0.5` 12 | 13 | DEBIAN Package 14 | =============================================================================== 15 | To build a DEB package, run `fakeroot debian/rules binary` in the root. 16 | 17 | PECL Package 18 | =============================================================================== 19 | You must have PEAR/PECL Installed in your system, and then `pear package` in the root 20 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension tarantool 2 | dnl set C compiler to C99 mode 3 | AC_PROG_CC_C99 4 | PHP_ARG_ENABLE(tarantool, for tarantool support, 5 | [ --enable-tarantool Enable tarantool support]) 6 | 7 | if test "$PHP_TARANTOOL" != "no"; then 8 | PHP_NEW_EXTENSION(tarantool, \ 9 | src/tarantool.c \ 10 | src/tarantool_network.c \ 11 | src/tarantool_msgpack.c \ 12 | src/tarantool_schema.c \ 13 | src/tarantool_proto.c \ 14 | src/tarantool_tp.c \ 15 | src/tarantool_exception.c \ 16 | src/utils.c \ 17 | src/third_party/msgpuck.c \ 18 | src/third_party/sha1.c \ 19 | src/third_party/PMurHash.c \ 20 | , $ext_shared) 21 | PHP_ADD_BUILD_DIR([$ext_builddir/src/]) 22 | PHP_ADD_BUILD_DIR([$ext_builddir/src/third_party]) 23 | fi 24 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | php-tarantool (1.6-1) unstable; urgency=medium 2 | 3 | * New upstream version. 4 | * Relicensed as BSD. closes: #752655. 5 | 6 | -- Dmitry E. Oboukhov Mon, 27 Oct 2014 18:46:34 +0300 7 | 8 | php-tarantool (1.0-2) unstable; urgency=low 9 | 10 | * Fixed debian/rules:clean section by ftp-master's review. 11 | 12 | -- Dmitry E. Oboukhov Tue, 15 Apr 2014 22:29:26 +0400 13 | 14 | php-tarantool (1.0-1) unstable; urgency=low 15 | 16 | * Initial release. (Closes: #731850) 17 | 18 | -- Dmitry E. Oboukhov Mon, 09 Dec 2013 18:38:37 +0400 19 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control.in: -------------------------------------------------------------------------------- 1 | Source: php-tarantool 2 | Priority: optional 3 | Maintainer: Dmitry E. Oboukhov 4 | Build-Depends: debhelper (>= 8), 5 | php-all-dev, 6 | pkg-config 7 | Section: php 8 | Standards-Version: 3.9.4 9 | Homepage: https://github.com/tarantool/tarantool-php 10 | VCS-Browser: https://github.com/tarantool/tarantool-php 11 | VCS-Git: git://github.com/tarantool/tarantool-php.git 12 | 13 | # Package name is preprocessed in debian/prebuild.sh. 14 | Package: php${phpversion}-tarantool 15 | Architecture: any 16 | Priority: optional 17 | Conflicts: libtarantool-php 18 | Replaces: libtarantool-php 19 | Provides: php-tarantool 20 | Depends: ${php:Depends}, ${misc:Depends}, ${shlibs:Depends} 21 | Suggests: tarantool 22 | Description: PECL PHP driver for Tarantool/Box 23 | Tarantool is an in-memory database and Lua application server. 24 | This package provides PECL PHP driver for Tarantool/Box. 25 | 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://dep.debian.net/deps/dep5/ 2 | Debianized-By: Dmitry E. Oboukhov 3 | Upstream-Name: tarantool-php 4 | Upstream-Contact: 5 | Source: https://github.com/tarantool/tarantool-php 6 | 7 | Files: * 8 | Copyright: 2014 by 9 | Aleksey Demakov, Aleksey Mashanov, Alexandre Kalendarev, 10 | Damien Lefortier, Dmitry E. Oboukhov, Dmitry Simonenko, 11 | Konstantin Osipov, Konstantin Shulgin, Mons Anderson, 12 | Pavel Cherenkov, Roman Antipin, Roman Tokarev, Roman Tsisyk, 13 | Teodor Sigaev, Timofey Khryukin, Yuriy Nevinitsin, Yuriy Vostrikov, 14 | Veniamin Gvozdikov, Aleksandr Lyapunov 15 | License: BSD-2-Clause 16 | Redistribution and use in source and binary forms, with or 17 | without modification, are permitted provided that the following 18 | conditions are met: 19 | . 20 | 1. Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the 22 | following disclaimer. 23 | . 24 | 2. Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | . 29 | THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND 30 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 33 | AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 34 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 36 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 37 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 38 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 40 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41 | SUCH DAMAGE. 42 | -------------------------------------------------------------------------------- /debian/php-tarantool.postinst.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ "$1" = "configure" ]; then 6 | if [ -e /usr/lib/php/php-maintscript-helper ] ; then 7 | . /usr/lib/php/php-maintscript-helper 8 | php_invoke enmod ${phpversion} ALL tarantool 9 | fi 10 | fi 11 | 12 | exit 0 13 | -------------------------------------------------------------------------------- /debian/php-tarantool.postrm.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ "$1" = "remove" ]; then 6 | if [ -e /usr/lib/php/php-maintscript-helper ] ; then 7 | . /usr/lib/php/php-maintscript-helper 8 | php_invoke dismod ${phpversion} ALL tarantool 9 | fi 10 | fi 11 | 12 | exit 0 13 | -------------------------------------------------------------------------------- /debian/prebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -exu # Strict shell (w/o -o pipefail) 4 | 5 | # Preserve DEBIAN_FRONTEND=noninteractive. It prevents packages 6 | # like tzdata from asking configuration parameters interactively. 7 | # 8 | # See https://github.com/packpack/packpack/issues/7 9 | SUDO="sudo -E" 10 | 11 | ${SUDO} apt-get update > /dev/null 12 | ${SUDO} apt-get -y install php-all-dev 13 | 14 | phpversion=$(php-config --version | sed 's/^\([0-9]\+\.[0-9]\).*/\1/') 15 | 16 | cd /build/php-tarantool-* 17 | sed -e "s/\${phpversion}/${phpversion}/" debian/control.in > debian/control 18 | rm debian/control.in 19 | 20 | sed -e "s/\${phpversion}/${phpversion}/" debian/php-tarantool.postinst.in \ 21 | > debian/php${phpversion}-tarantool.postinst 22 | sed -e "s/\${phpversion}/${phpversion}/" debian/php-tarantool.postrm.in \ 23 | > debian/php${phpversion}-tarantool.postrm 24 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | include /usr/share/cdbs/1/rules/debhelper.mk 4 | 5 | DEB_PACKAGE := $(strip $(shell egrep '^Package: ' debian/control | \ 6 | cut -f 2 -d ':')) 7 | 8 | phpversion = $(shell echo $(DEB_PACKAGE) | \ 9 | sed -e 's/^php\([0-9.]\+\)-tarantool$$/\1/') 10 | phpapi = $(shell php-config$(phpversion) --phpapi) 11 | version = $(shell dpkg-parsechangelog \ 12 | |grep ^Version|awk '{print $$2}'|sed 's/-.*//') 13 | 14 | makebuilddir/php$(phpversion)-tarantool:: 15 | phpize 16 | ./configure 17 | make 18 | echo "php:Depends=phpapi-$(phpapi)" > debian/php$(phpversion)-tarantool.substvars 19 | 20 | install/php$(phpversion)-tarantool:: 21 | install -m 0755 -d debian/php$(phpversion)-tarantool/usr/lib/php/$(phpapi)/ 22 | install -m 0755 -d debian/php$(phpversion)-tarantool/etc/php/$(phpversion)/mods-available/ 23 | install -m 0755 modules/tarantool.so \ 24 | debian/php$(phpversion)-tarantool/usr/lib/php/$(phpapi)/ 25 | echo extension=tarantool.so \ 26 | > debian/php$(phpversion)-tarantool/etc/php/$(phpversion)/mods-available/tarantool.ini 27 | 28 | clean:: 29 | phpize --clean 30 | rm -f debian/debhelper.log 31 | rm -f debian/*.substvars 32 | rm -f src/tarantool.lo 33 | rm -fr src/.libs 34 | 35 | 36 | 37 | tarball: clean 38 | cd .. \ 39 | && tar \ 40 | --exclude=debian \ 41 | --exclude=.git \ 42 | -czvf php-tarantool_$(version).orig.tar.gz \ 43 | php-tarantool-$(version) 44 | 45 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | --extend-diff-ignore=build 2 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tarantool/tarantool-php/1e1f6eb7fc7bb61b5ccefc45d5b9817981582b1a/lib/__init__.py -------------------------------------------------------------------------------- /lib/tarantool_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import errno 3 | import shlex 4 | import random 5 | import socket 6 | import tempfile 7 | 8 | import yaml 9 | import time 10 | import shutil 11 | import subprocess 12 | 13 | def check_port(port, rais=True): 14 | try: 15 | sock = socket.create_connection(("localhost", port)) 16 | except socket.error: 17 | return True 18 | if rais: 19 | raise RuntimeError("The server is already running on port {0}".format(port)) 20 | return False 21 | 22 | def find_port(port = None): 23 | if port is None: 24 | port = random.randrange(3300, 9999) 25 | while port < 9999: 26 | if check_port(port, False): 27 | return port 28 | port += 1 29 | return find_port(3300) 30 | 31 | 32 | class RunnerException(object): 33 | pass 34 | 35 | 36 | class TarantoolAdmin(object): 37 | def __init__(self, host, port): 38 | self.host = host 39 | self.port = port 40 | self.is_connected = False 41 | self.socket = None 42 | 43 | def connect(self): 44 | self.socket = socket.create_connection((self.host, self.port)) 45 | self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) 46 | self.is_connected = True 47 | self.recv_exactly(128) 48 | 49 | def recv_exactly(self, size): 50 | if not self.is_connected: 51 | return False 52 | while (size > 0): 53 | response = self.socket.recv(size) 54 | size -= len(response) 55 | 56 | def disconnect(self): 57 | if self.is_connected: 58 | self.socket.close() 59 | self.socket = None 60 | self.is_connected = False 61 | 62 | def reconnect(self): 63 | self.disconnect() 64 | self.connect() 65 | 66 | def opt_reconnect(self): 67 | """ On a socket which was disconnected, recv of 0 bytes immediately 68 | returns with no data. On a socket which is alive, it returns EAGAIN. 69 | Make use of this property and detect whether or not the socket is 70 | dead. Reconnect a dead socket, do nothing if the socket is good.""" 71 | try: 72 | if self.socket is None or self.socket.recv(0, socket.MSG_DONTWAIT) == '': 73 | self.reconnect() 74 | except socket.error as e: 75 | if e.errno == errno.EAGAIN: 76 | pass 77 | else: 78 | self.reconnect() 79 | 80 | def execute(self, command): 81 | self.opt_reconnect() 82 | return self.execute_no_reconnect(command) 83 | 84 | def __enter__(self): 85 | self.connect() 86 | return self 87 | 88 | def __exit__(self, type, value, tb): 89 | self.disconnect() 90 | 91 | def __call__(self, command): 92 | return self.execute(command) 93 | 94 | def execute_no_reconnect(self, command): 95 | if not command: 96 | return 97 | cmd = command.replace('\n', ' ') + '\n' 98 | self.socket.sendall(cmd.encode()) 99 | 100 | bufsiz = 4096 101 | res = b"" 102 | 103 | while True: 104 | buf = self.socket.recv(bufsiz) 105 | if not buf: 106 | break 107 | res = res + buf 108 | if (res.rfind(b"\n...\n") >= 0 or res.rfind(b"\r\n...\r\n") >= 0): 109 | break 110 | 111 | return yaml.safe_load(res) 112 | 113 | class TarantoolServer(object): 114 | default_tarantool = { 115 | "bin": "tarantool", 116 | "logfile": "tarantool.log", 117 | "init": "init.lua"} 118 | 119 | default_cfg = { 120 | "custom_proc_title": "\"tarantool-python testing\"", 121 | "slab_alloc_arena": 0.5, 122 | "pid_file": "\"box.pid\"", 123 | "rows_per_wal": 200} 124 | 125 | @property 126 | def logfile_path(self): 127 | return os.path.join(self.vardir, self.default_tarantool['logfile']) 128 | 129 | @property 130 | def cfgfile_path(self): 131 | return os.path.join(self.vardir, self.default_tarantool['config']) 132 | 133 | @property 134 | def script_path(self): 135 | return os.path.join(self.vardir, self.default_tarantool['init']) 136 | 137 | @property 138 | def script_dst(self): 139 | return os.path.join(self.vardir, os.path.basename(self.script)) 140 | 141 | @property 142 | def script(self): 143 | if not hasattr(self, '_script'): self._script = None 144 | return self._script 145 | @script.setter 146 | def script(self, val): 147 | if val is None: 148 | if hasattr(self, '_script'): 149 | delattr(self, '_script') 150 | return 151 | self._script = os.path.abspath(val) 152 | 153 | @property 154 | def binary(self): 155 | if not hasattr(self, '_binary'): 156 | self._binary = self.find_exe() 157 | return self._binary 158 | 159 | @property 160 | def _admin(self): 161 | if not hasattr(self, 'admin'): 162 | self.admin = None 163 | return self.admin 164 | @_admin.setter 165 | def _admin(self, port): 166 | try: 167 | int(port) 168 | except ValueError: 169 | raise ValueError("Bad port number: '%s'" % port) 170 | if hasattr(self, 'admin'): 171 | del self.admin 172 | self.admin = TarantoolAdmin('127.0.0.1', port) 173 | 174 | @property 175 | def log_des(self): 176 | if not hasattr(self, '_log_des'): 177 | self._log_des = open(self.logfile_path, 'a') 178 | return self._log_des 179 | @log_des.deleter 180 | def log_des(self): 181 | if not hasattr(self, '_log_des'): 182 | return 183 | if not self._log_des.closed: 184 | self._log_des.close() 185 | delattr(self, '_log_des') 186 | 187 | def __init__(self): 188 | os.popen('ulimit -c unlimited') 189 | self.args = {} 190 | self.args['primary'] = find_port() 191 | self.args['admin'] = find_port(self.args['primary'] + 1) 192 | self._admin = self.args['admin'] 193 | self.vardir = tempfile.mkdtemp(prefix='var_', dir=os.getcwd()) 194 | self.find_exe() 195 | 196 | def find_exe(self): 197 | if 'TARANTOOL_BOX_PATH' in os.environ: 198 | os.environ["PATH"] = os.environ["TARANTOOL_BOX_PATH"] + os.pathsep + os.environ["PATH"] 199 | 200 | for _dir in os.environ["PATH"].split(os.pathsep): 201 | exe = os.path.join(_dir, self.default_tarantool["bin"]) 202 | if os.access(exe, os.X_OK): 203 | return os.path.abspath(exe) 204 | raise RuntimeError("Can't find server executable in " + os.environ["PATH"]) 205 | 206 | def generate_configuration(self): 207 | os.putenv("PRIMARY_PORT", str(self.args['primary'])) 208 | os.putenv("ADMIN_PORT", str(self.args['admin'])) 209 | 210 | def prepare_args(self): 211 | return shlex.split(self.binary if not self.script else self.script_dst) 212 | 213 | def wait_until_started(self): 214 | """ Wait until server is started. 215 | 216 | Server consists of two parts: 217 | 1) wait until server is listening on sockets 218 | 2) wait until server tells us his status 219 | """ 220 | 221 | while True: 222 | try: 223 | temp = TarantoolAdmin('127.0.0.1', self.args['admin']) 224 | ans = temp('box.info.status')[0] 225 | if ans in ('running', 'primary', 'hot_standby', 'orphan', 'loading') or ans.startswith('replica'): 226 | return True 227 | else: 228 | raise Exception("Strange output for `box.info.status`: %s" % (ans)) 229 | except socket.error as e: 230 | if e.errno == errno.ECONNREFUSED: 231 | time.sleep(0.1) 232 | continue 233 | raise 234 | 235 | def start(self): 236 | # Main steps for running Tarantool\Box 237 | # * Find binary file --DONE(find_exe -> binary) 238 | # * Create vardir --DONE(__init__) 239 | # * Generate cfgfile --DONE(generate_configuration) 240 | # * (MAYBE) Copy init.lua --INSIDE 241 | # * Concatenate arguments and 242 | # start Tarantool\Box --DONE(prepare_args) 243 | # * Wait unitl Tarantool\Box 244 | # started --DONE(wait_until_started) 245 | self.generate_configuration() 246 | if self.script: 247 | shutil.copy(self.script, self.script_dst) 248 | os.chmod(self.script_dst, 0o777) 249 | args = self.prepare_args() 250 | self.process = subprocess.Popen(args, 251 | cwd = self.vardir, 252 | stdout=self.log_des, 253 | stderr=self.log_des) 254 | self.wait_until_started() 255 | 256 | def stop(self): 257 | if self.process and self.process.poll() is None: 258 | self.process.terminate() 259 | self.process.wait() 260 | 261 | def restart(self): 262 | self.stop() 263 | self.start() 264 | 265 | def clean(self): 266 | if os.path.exists(self.vardir): 267 | shutil.rmtree(self.vardir) 268 | 269 | def __del__(self): 270 | self.stop() 271 | self.clean() 272 | 273 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | Tarantool 7 | tarantool.github.io/tarantool-php/pecl 8 | PHP extension for Tarantool 9 | 10 | PECL PHP driver for Tarantool 11 | Tarantool is an in-memory database and Lua application server. 12 | This package provides PECL PHP driver for Tarantool. 13 | 14 | 15 | Eugine Blikh 16 | bigbes 17 | bigbes@gmail.com 18 | yes 19 | 20 | 2024-07-04 21 | 22 | 0.4.0 23 | 0.4.0 24 | 25 | 26 | beta 27 | beta 28 | 29 | BSD 2-Clause 30 | - 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 7.0.0 83 | 8.0.0 84 | 6.0.0 85 | 86 | 87 | 1.4.0 88 | 89 | 90 | 91 | tarantool 92 | 93 | 94 | 95 | betabeta 96 | 0.4.00.4.0 97 | 2024-07-04 98 | 99 | tarantool-php 0.4.0 100 | 101 | ## Overview 102 | 103 | The release adds support for PHP 8.x and fixes incompatibility with 104 | Tarantool 2.10+. 105 | 106 | ## Breaking changes 107 | 108 | This release should not break existing code. 109 | 110 | ## New features 111 | 112 | - Support PHP 8.x (#171). 113 | - Support Tarantool 2.10+ (#175). 114 | - Call IPROTO method (#101). 115 | - Support new _index system space format (#151). 116 | 117 | ## Bugfixes 118 | 119 | - Give meaningful error when retry_count is zero (#83). 120 | - select() by space_no and index_name (#42). 121 | 122 | 123 | 124 | betabeta 125 | 0.3.30.3.3 126 | 2020-06-30 127 | 128 | tarantool-php 0.3.3 129 | 130 | ## Overview 131 | 132 | This release revives the connector in fact: now it supports PHP 7.0-7.4 and 133 | Tarantool 1.6-2.5 (except 2.2 for now due to #151). There are several 134 | restrictions, however. Most notable are: 135 | 136 | - Schema fetching fails on a space with `is_nullable` or `collation` index 137 | part parameters (#151). 138 | - New call_17 command is not supported yet (#101). 139 | 140 | Those problem will be fixed in the next release. Anyway, this release allows 141 | to upgrade PHP and Tarantool versions and keep existing PHP code, which uses 142 | the connector, operational. 143 | 144 | ## Breaking changes 145 | 146 | This release should not break existing code. 147 | 148 | ## New features 149 | 150 | - Support PHP 7.3 and 7.4 (#139, #150). 151 | 152 | ## Bugfixes 153 | 154 | - Ignore unknown fields in a space format (PR #132). 155 | - Fixed 'Undefined property' error at attempt to extend Tarantool class 156 | (#135). 157 | - Raise 'No field ... defined' at attempt to update by an unknown field name 158 | (b3846f6a). 159 | - Allow `null` as `offset` value for select() when strict types are enabled 160 | (#154). 161 | 162 | ## Testing and deployment 163 | 164 | - Support both Python 2 and 3 in the test runner (9bc7fa5f). 165 | - Use strict types mode in testing (da8ecba8). 166 | - Support PHPUnit 6-9 for testing against different PHP versions. 167 | - Enabled PHP 7.0-7.4 in testing. 168 | - Enabled Tarantool 1.6-2.5 in testing (except 2.2 for now). 169 | - Added 'php-tarantool' alias for Debian / Ubuntu packages (PR #155). 170 | - Enable the extension by default on Debian / Ubuntu (40d8795a, PR #156). 171 | - Deploy packages for Linux distributions with PHP 7 (#117): 172 | - CentOS 8; 173 | - Fedora 25-31; 174 | - Debian: Stretch, Buster; 175 | - Ubuntu: Xenial, Bionic, Eoan, Focal. 176 | 177 | 178 | 179 | betabeta 180 | 0.3.20.3.2 181 | 2018-04-18 182 | 183 | tarantool-php 0.3.2 184 | 185 | Upsert bug fixed 186 | 187 | 188 | 189 | betabeta 190 | 0.2.00.2.0 191 | 2016-11-22 192 | 193 | tarantool-php 0.2.0 194 | 195 | PHP 7 version 196 | 197 | 198 | 199 | betabeta 200 | 0.1.00.1.0 201 | 2016-02-26 202 | 203 | tarantool-php 0.1.0 204 | 205 | Fix a lot of bugs, added upsert command, added tests 206 | 207 | 208 | 209 | betabeta 210 | 0.0.130.0.13 211 | 2015-04-24 212 | 213 | tarantool-php 0.0.13 214 | 215 | Schema is rewritten (using mhash, not zend hash). 216 | Batching auth+select_index+select_space (download schema) using tp.h 217 | 218 | 219 | 220 | betabeta 221 | 0.0.120.0.12 222 | 2015-04-24 223 | 224 | tarantool-php 0.0.12 225 | 226 | Fix couple of segfaults and memory leaks 227 | 228 | 229 | 230 | betabeta 231 | 0.0.110.0.11 232 | 2015-04-22 233 | 234 | tarantool-php 0.0.11 235 | 236 | Fix select error on 32bit systems, update version of rpm packages 237 | 238 | 239 | 240 | betabeta 241 | 0.0.100.0.10 242 | 2015-03-24 243 | 244 | tarantool-php 0.0.10 245 | 246 | Add alias for flush_schema -> flushSchema 247 | Add eval to commands 248 | Fix segfaults when args are empty in eval/call 249 | Add RTree constants 250 | 251 | 252 | 253 | betabeta 254 | 0.0.90.0.9 255 | 2015-03-24 256 | 257 | tarantool-php 0.0.9 258 | 259 | Fix for build with pecl 260 | 261 | 262 | 263 | betabeta 264 | 0.0.80.0.8 265 | 2015-03-24 266 | 267 | tarantool-php 0.0.8 268 | 269 | Fixing avito segfaults, remove obsolete cleanups 270 | 271 | 272 | 273 | betabeta 274 | 0.0.70.0.7 275 | 2015-03-17 276 | 277 | tarantool-php 0.0.7 278 | 279 | Fixing avito segfaults 280 | 281 | 282 | 283 | betabeta 284 | 0.0.60.0.5 285 | 2015-03-08 286 | 287 | tarantool-php 0.0.6 288 | 289 | Fixing some segfaults 290 | 291 | 292 | 293 | betabeta 294 | 0.0.50.0.5 295 | 2014-02-09 296 | 297 | tarantool-php 0.0.5 298 | 299 | 300 | 301 | betabeta 302 | 0.0.40.0.4 303 | 2014-08-11 304 | 305 | tarantool-php 0.0.4 306 | 307 | Added connection pool for PHP-FPM, fixing memleaks, fixing msgpack 308 | 309 | 310 | 311 | betabeta 312 | 0.0.30.0.3 313 | 2014-08-11 314 | 315 | tarantool-php 0.0.3 316 | 317 | Continue with Tests, Fixing Bugs 318 | Adding global constants for different iterator types 319 | 320 | 321 | 322 | betabeta 323 | 0.0.20.0.2 324 | 2014-08-07 325 | 326 | tarantool-php 0.0.2 327 | 328 | Initial Release 329 | 330 | 331 | 332 | 333 | 334 | -------------------------------------------------------------------------------- /php_tarantool.h: -------------------------------------------------------------------------------- 1 | src/php_tarantool.h -------------------------------------------------------------------------------- /rpm/php-tarantool.spec: -------------------------------------------------------------------------------- 1 | %global php_apiver %((echo 0; php -i 2>/dev/null | sed -n 's/^PHP API => //p') | tail -1) 2 | %global php_extdir %(php-config --extension-dir 2>/dev/null || echo "undefined") 3 | %global php_version %(php-config --version 2>/dev/null || echo 0) 4 | 5 | Name: php-tarantool 6 | Version: 0.2.0.0 7 | Release: 1%{?dist} 8 | Summary: PECL PHP driver for Tarantool/Box 9 | Group: Development/Languages 10 | License: BSD 2-Clause 11 | URL: https://github.com/tarantool/tarantool-php/ 12 | Source0: tarantool-php-%{version}.tar.gz 13 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 14 | BuildRequires: php-devel 15 | Requires: php(zend-abi) = %{php_zend_api} 16 | Requires: php(api) = %{php_apiver} 17 | 18 | %global ini_name 50-tarantool.ini 19 | 20 | %description 21 | PECL PHP driver for Tarantool/Box 22 | Tarantool is an in-memory database and Lua application server. 23 | This package provides PECL PHP driver for Tarantool/Box. 24 | 25 | %prep 26 | %setup -q -n tarantool-php-%{version} 27 | 28 | cat > %{ini_name} << 'EOF' 29 | ; Enable tarantool extension module 30 | extension=tarantool.so 31 | 32 | ; ----- Configuration options 33 | ; https://github.com/tarantool/tarantool-php/README.md 34 | 35 | EOF 36 | 37 | %build 38 | %{_bindir}/phpize 39 | %configure 40 | make %{?_smp_mflags} 41 | 42 | %install 43 | rm -rf $RPM_BUILD_ROOT 44 | make install INSTALL_ROOT=$RPM_BUILD_ROOT 45 | # Drop in the bit of configuration 46 | install -D -m 644 %{ini_name} %{buildroot}%{php_inidir}/%{ini_name} 47 | 48 | %clean 49 | rm -rf $RPM_BUILD_ROOT 50 | 51 | %files 52 | %defattr(-,root,root,-) 53 | %{php_extdir}/tarantool.so 54 | %config(noreplace) %{php_inidir}/%{ini_name} 55 | -------------------------------------------------------------------------------- /src/php_tarantool.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TARANTOOL_H 2 | #define PHP_TARANTOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #ifdef HAVE_CONFIG_H 17 | # include "config.h" 18 | #endif 19 | 20 | #if PHP_VERSION_ID >= 70200 21 | # include 22 | # define smart_string_alloc4(d, n, what, newlen) newlen = smart_string_alloc(d, n, what) 23 | #elif PHP_VERSION_ID >= 70000 24 | # include 25 | #else 26 | # include 27 | typedef smart_str smart_string; 28 | # define smart_string_alloc4(...) smart_str_alloc4(__VA_ARGS__) 29 | # define smart_string_free_ex(...) smart_str_free_ex(__VA_ARGS__) 30 | #endif 31 | 32 | #if PHP_VERSION_ID >= 80000 33 | /* These macroses were removed in PHP 8. */ 34 | # define TSRMLS_CC 35 | # define TSRMLS_DC 36 | # define TSRMLS_FETCH() 37 | #endif /* PHP_VERSION_ID >= 80000 */ 38 | 39 | #if PHP_VERSION_ID < 70300 40 | # define GC_SET_REFCOUNT(p, rc) do { \ 41 | GC_REFCOUNT((p)) = (rc); \ 42 | } while(0) 43 | #endif /* PHP_VERSION_ID < 70300 */ 44 | 45 | #if PHP_VERSION_ID < 70300 46 | 47 | # define ARRAY_PROTECT_RECURSION(data) do { \ 48 | if (Z_TYPE_P((data)) == IS_ARRAY && \ 49 | ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P((data)))) \ 50 | { \ 51 | Z_ARRVAL_P((data))->u.v.nApplyCount++; \ 52 | } \ 53 | } while(0) 54 | 55 | # define ARRAY_UNPROTECT_RECURSION(data) do { \ 56 | if (Z_TYPE_P((data)) == IS_ARRAY && \ 57 | ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P((data)))) \ 58 | { \ 59 | Z_ARRVAL_P((data))->u.v.nApplyCount--; \ 60 | } \ 61 | } while(0) 62 | 63 | # define ARRAY_IS_RECURSIVE(data) ( \ 64 | Z_TYPE_P(data) == IS_ARRAY && \ 65 | ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P((data))) && \ 66 | Z_ARRVAL_P((data))->u.v.nApplyCount > 1 \ 67 | ) 68 | 69 | #else /* PHP_VERSION_ID < 70300 */ 70 | 71 | # define ARRAY_PROTECT_RECURSION(data) do { \ 72 | if (Z_TYPE_P((data)) == IS_ARRAY && \ 73 | !(GC_FLAGS(Z_ARRVAL_P((data))) & GC_IMMUTABLE)) \ 74 | { \ 75 | GC_PROTECT_RECURSION(Z_ARRVAL_P((data))); \ 76 | } \ 77 | } while(0) 78 | 79 | # define ARRAY_UNPROTECT_RECURSION(data) do { \ 80 | if (Z_TYPE_P((data)) == IS_ARRAY && \ 81 | !(GC_FLAGS(Z_ARRVAL_P((data))) & GC_IMMUTABLE)) \ 82 | { \ 83 | GC_UNPROTECT_RECURSION(Z_ARRVAL_P((data))); \ 84 | } \ 85 | } while(0) 86 | 87 | # define ARRAY_IS_RECURSIVE(data) ( \ 88 | Z_TYPE_P((data)) == IS_ARRAY && \ 89 | !(GC_FLAGS(Z_ARRVAL_P((data))) & GC_IMMUTABLE) && \ 90 | GC_IS_RECURSIVE(Z_ARRVAL_P((data))) \ 91 | ) 92 | 93 | #endif /* !(PHP_VERSION_ID < 70300)) */ 94 | 95 | extern zend_module_entry tarantool_module_entry; 96 | #define phpext_tarantool_ptr &tarantool_module_entry 97 | 98 | #define PHP_TARANTOOL_VERSION "0.4.0" 99 | #define PHP_TARANTOOL_EXTNAME "tarantool" 100 | 101 | #ifdef PHP_WIN32 102 | # define PHP_TARANTOOL_API __declspec(__dllexport) 103 | #elif defined(__GNUC__) && __GNUC__ >= 4 104 | # define PHP_TARANTOOL_API __attribute__ ((visibility("default"))) 105 | #else 106 | # define PHP_TARANTOOL_API 107 | #endif 108 | 109 | #define TARANTOOL_TIMEOUT_SEC 10 110 | #define TARANTOOL_TIMEOUT_USEC 0 111 | 112 | #ifdef ZTS 113 | #include "TSRM.h" 114 | #endif 115 | 116 | struct tarantool_schema; 117 | 118 | #define SSTR_BEG(str) (str->c) 119 | #define SSTR_END(str) (str->c + str->a) 120 | #define SSTR_AWA(str) (str->a) 121 | #define SSTR_LEN(str) (str->len) 122 | #define SSTR_POS(str) (str->c + str->len) 123 | #define SSTR_DIF(str, end) (end - str->c) 124 | 125 | PHP_MINIT_FUNCTION(tarantool); 126 | PHP_RINIT_FUNCTION(tarantool); 127 | PHP_MSHUTDOWN_FUNCTION(tarantool); 128 | PHP_MINFO_FUNCTION(tarantool); 129 | 130 | PHP_METHOD(Tarantool, __construct); 131 | PHP_METHOD(Tarantool, connect); 132 | PHP_METHOD(Tarantool, reconnect); 133 | PHP_METHOD(Tarantool, close); 134 | PHP_METHOD(Tarantool, authenticate); 135 | PHP_METHOD(Tarantool, ping); 136 | PHP_METHOD(Tarantool, select); 137 | PHP_METHOD(Tarantool, insert); 138 | PHP_METHOD(Tarantool, replace); 139 | PHP_METHOD(Tarantool, call); 140 | PHP_METHOD(Tarantool, eval); 141 | PHP_METHOD(Tarantool, delete); 142 | PHP_METHOD(Tarantool, update); 143 | PHP_METHOD(Tarantool, upsert); 144 | PHP_METHOD(Tarantool, flush_schema); 145 | 146 | ZEND_BEGIN_MODULE_GLOBALS(tarantool) 147 | zend_bool persistent; 148 | zend_bool use_namespace; 149 | zend_bool connection_alias; 150 | long sync_counter; 151 | long retry_count; 152 | double retry_sleep; 153 | double timeout; 154 | double request_timeout; 155 | ZEND_END_MODULE_GLOBALS(tarantool) 156 | 157 | ZEND_EXTERN_MODULE_GLOBALS(tarantool); 158 | 159 | typedef struct tarantool_object { 160 | struct tarantool_connection { 161 | char *host; 162 | int port; 163 | char *login; 164 | char *passwd; 165 | php_stream *stream; 166 | struct tarantool_schema *schema; 167 | smart_string *value; 168 | struct tp *tps; 169 | char *greeting; 170 | char *salt; 171 | /* Only for persistent connections */ 172 | char *orig_login; 173 | char *suffix; 174 | int suffix_len; 175 | zend_string *persistent_id; 176 | } *obj; 177 | 178 | zend_bool is_persistent; 179 | 180 | zend_object zo; 181 | } tarantool_object; 182 | 183 | typedef struct tarantool_connection tarantool_connection; 184 | 185 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_ce(void); 186 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_exception(void); 187 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_ioexception(void); 188 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_clienterror(void); 189 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_parsingexception(void); 190 | PHP_TARANTOOL_API zend_class_entry *php_tarantool_get_exception_base(int root TSRMLS_DC); 191 | 192 | #ifdef ZTS 193 | # define TARANTOOL_G(v) TSRMG(tarantool_globals_id, zend_tarantool_globals *, v) 194 | #else 195 | # define TARANTOOL_G(v) (tarantool_globals.v) 196 | #endif 197 | 198 | #endif /* PHP_TARANTOOL_H */ 199 | -------------------------------------------------------------------------------- /src/tarantool_exception.c: -------------------------------------------------------------------------------- 1 | #include "php_tarantool.h" 2 | 3 | #include "tarantool_internal.h" 4 | 5 | #include "tarantool_exception.h" 6 | 7 | zend_class_entry *TarantoolException_ptr; 8 | zend_class_entry *TarantoolIOException_ptr; 9 | zend_class_entry *TarantoolClientError_ptr; 10 | zend_class_entry *TarantoolParsingException_ptr; 11 | 12 | #if HAVE_SPL 13 | /* Pointer to SPL Runtime Exception */ 14 | static zend_class_entry *SPLRE_ptr = NULL; 15 | static const char SPLRE_name[] = "runtimeexception"; 16 | static size_t SPLRE_name_len = sizeof(SPLRE_name); 17 | #endif 18 | 19 | PHP_TARANTOOL_API 20 | zend_class_entry *php_tarantool_get_exception(void) 21 | { 22 | return TarantoolException_ptr; 23 | } 24 | 25 | PHP_TARANTOOL_API 26 | zend_class_entry *php_tarantool_get_ioexception(void) 27 | { 28 | return TarantoolIOException_ptr; 29 | } 30 | 31 | PHP_TARANTOOL_API 32 | zend_class_entry *php_tarantool_get_clienterror(void) 33 | { 34 | return TarantoolClientError_ptr; 35 | } 36 | 37 | PHP_TARANTOOL_API 38 | zend_class_entry *php_tarantool_get_parsingexception(void) 39 | { 40 | return TarantoolParsingException_ptr; 41 | } 42 | 43 | PHP_TARANTOOL_API 44 | zend_class_entry *php_tarantool_get_exception_base(int root) { 45 | TSRMLS_FETCH(); 46 | #if HAVE_SPL 47 | if (!root) { 48 | if (SPLRE_ptr == NULL) { 49 | zval *pce_z = NULL; 50 | zend_string *key = zend_string_init(SPLRE_name, 51 | SPLRE_name_len, 0); 52 | if ((pce_z = zend_hash_find(CG(class_table), 53 | key)) != NULL) 54 | SPLRE_ptr = Z_CE_P(pce_z); 55 | zend_string_release(key); 56 | } 57 | if (SPLRE_ptr != NULL) 58 | return SPLRE_ptr; 59 | } 60 | #endif 61 | return zend_ce_exception; 62 | } 63 | 64 | zend_object *tarantool_throw_exception_vbase(zend_class_entry *ce, 65 | uint32_t code, const char *fmt, 66 | va_list arg) { 67 | char *message; 68 | zend_object *obj; 69 | 70 | vspprintf(&message, 0, fmt, arg); 71 | va_end(arg); 72 | obj = zend_throw_exception(ce, (const char *)message, code TSRMLS_DC); 73 | efree(message); 74 | return obj; 75 | } 76 | 77 | zend_object *tarantool_throw_exception_base(zend_class_entry *ce, uint32_t code, 78 | const char *fmt, ...) { 79 | va_list arg; 80 | va_start(arg, fmt); 81 | 82 | return tarantool_throw_exception_vbase(ce, code, fmt, arg); 83 | } 84 | 85 | zend_object *tarantool_throw_exception(const char *fmt, ...) { 86 | va_list arg; 87 | va_start(arg, fmt); 88 | 89 | return tarantool_throw_exception_vbase(TarantoolException_ptr, 0, fmt, 90 | arg); 91 | } 92 | 93 | zend_object *tarantool_throw_ioexception(const char *fmt, ...) { 94 | va_list arg; 95 | va_start(arg, fmt); 96 | 97 | return tarantool_throw_exception_vbase(TarantoolIOException_ptr, 0, fmt, 98 | arg); 99 | } 100 | 101 | zend_object *tarantool_throw_clienterror(uint32_t code, const char *err, 102 | size_t errlen) { 103 | return tarantool_throw_exception_base(TarantoolClientError_ptr, code, 104 | "%.*s", errlen, err); 105 | } 106 | 107 | zend_object *tarantool_throw_parsingexception(const char *component) { 108 | return tarantool_throw_exception_base(TarantoolParsingException_ptr, 0, 109 | "Failed to parse %s", component); 110 | } 111 | 112 | ZEND_MINIT_FUNCTION(tarantool_exception) { 113 | /* Init class entries */ 114 | zend_class_entry tarantool_xc_class; 115 | zend_class_entry tarantool_io_xc_class; 116 | zend_class_entry tarantool_client_er_class; 117 | zend_class_entry tarantool_parsing_xc_class; 118 | 119 | TNT_INIT_CLASS_ENTRY(tarantool_xc_class, "TarantoolException", 120 | "Tarantool\\Exception", NULL); 121 | TarantoolException_ptr = zend_register_internal_class_ex( 122 | &tarantool_xc_class, 123 | php_tarantool_get_exception_base(0) 124 | ); 125 | TNT_INIT_CLASS_ENTRY(tarantool_io_xc_class, "TarantoolIOException", 126 | "Tarantool\\Exception\\IOException", NULL); 127 | TarantoolIOException_ptr = zend_register_internal_class_ex( 128 | &tarantool_io_xc_class, 129 | TarantoolException_ptr 130 | ); 131 | TNT_INIT_CLASS_ENTRY(tarantool_client_er_class, "TarantoolClientError", 132 | "Tarantool\\Exception\\ClientError", NULL); 133 | TarantoolClientError_ptr = zend_register_internal_class_ex( 134 | &tarantool_client_er_class, 135 | TarantoolException_ptr 136 | ); 137 | TNT_INIT_CLASS_ENTRY(tarantool_parsing_xc_class, 138 | "TarantoolParsingException", 139 | "Tarantool\\Exception\\ParsingException", 140 | NULL); 141 | TarantoolParsingException_ptr = zend_register_internal_class_ex( 142 | &tarantool_parsing_xc_class, 143 | TarantoolException_ptr 144 | ); 145 | 146 | return SUCCESS; 147 | } 148 | -------------------------------------------------------------------------------- /src/tarantool_exception.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_EXCEPTION_H 2 | #define PHP_TNT_EXCEPTION_H 3 | 4 | extern zend_class_entry *TarantoolException_ptr; 5 | extern zend_class_entry *TarantoolIOException_ptr; 6 | extern zend_class_entry *TarantoolClientError_ptr; 7 | extern zend_class_entry *TarantoolParsingException_ptr; 8 | 9 | zend_object *tarantool_throw_exception(const char *fmt, ...); 10 | zend_object *tarantool_throw_ioexception(const char *fmt, ...); 11 | zend_object *tarantool_throw_clienterror(uint32_t code, const char *err, 12 | size_t errlen); 13 | zend_object *tarantool_throw_parsingexception(const char *component); 14 | 15 | ZEND_MINIT_FUNCTION(tarantool_exception); 16 | 17 | #define THROW_EXC(...) tarantool_throw_exception(__VA_ARGS__) 18 | 19 | #endif /* PHP_TNT_EXCEPTION_H */ 20 | -------------------------------------------------------------------------------- /src/tarantool_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_INTERNAL_H 2 | #define PHP_TNT_INTERNAL_H 3 | 4 | #define TNT_INIT_CLASS_ENTRY(ce, name, name_ns, methods) \ 5 | if (TARANTOOL_G(use_namespace)) { \ 6 | INIT_CLASS_ENTRY(ce, name_ns, methods); \ 7 | } else { \ 8 | INIT_CLASS_ENTRY(ce, name, methods); \ 9 | } 10 | 11 | #endif /* PHP_TNT_INTERNAL_H */ 12 | -------------------------------------------------------------------------------- /src/tarantool_msgpack.c: -------------------------------------------------------------------------------- 1 | #include "php_tarantool.h" 2 | 3 | #include "tarantool_msgpack.h" 4 | #include "tarantool_exception.h" 5 | 6 | #include "third_party/msgpuck.h" 7 | 8 | #ifndef HASH_KEY_NON_EXISTENT 9 | #define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT 10 | #endif /* HASH_KEY_NON_EXISTENT */ 11 | 12 | /* UTILITES */ 13 | 14 | int smart_string_ensure(smart_string *str, size_t len) { 15 | if (SSTR_AWA(str) > SSTR_LEN(str) + len) 16 | return 0; 17 | size_t needed = SSTR_AWA(str) * 2; 18 | if (SSTR_LEN(str) + len > needed) 19 | needed = SSTR_LEN(str) + len; 20 | register size_t __n1; 21 | smart_string_alloc4(str, needed, 1, __n1); 22 | if (SSTR_BEG(str) == NULL) 23 | return -1; 24 | return 0; 25 | } 26 | 27 | void smart_string_nullify(smart_string *str) { 28 | memset(SSTR_BEG(str), 0, SSTR_AWA(str)); 29 | } 30 | 31 | /* PACKING ROUTINES */ 32 | 33 | void php_mp_pack_nil(smart_string *str) { 34 | size_t needed = mp_sizeof_nil(); 35 | smart_string_ensure(str, needed); 36 | mp_encode_nil(SSTR_POS(str)); 37 | SSTR_LEN(str) += needed; 38 | } 39 | 40 | void php_mp_pack_long_pos(smart_string *str, long val) { 41 | size_t needed = mp_sizeof_uint(val); 42 | smart_string_ensure(str, needed); 43 | mp_encode_uint(SSTR_POS(str), val); 44 | SSTR_LEN(str) += needed; 45 | } 46 | 47 | void php_mp_pack_long_neg(smart_string *str, long val) { 48 | size_t needed = mp_sizeof_int(val); 49 | smart_string_ensure(str, needed); 50 | mp_encode_int(SSTR_POS(str), val); 51 | SSTR_LEN(str) += needed; 52 | } 53 | 54 | void php_mp_pack_long(smart_string *str, long val) { 55 | if (val >= 0) 56 | php_mp_pack_long_pos(str, val); 57 | else 58 | php_mp_pack_long_neg(str, val); 59 | } 60 | 61 | void php_mp_pack_double(smart_string *str, double val) { 62 | size_t needed = mp_sizeof_double(val); 63 | smart_string_ensure(str, needed); 64 | mp_encode_double(SSTR_POS(str), val); 65 | SSTR_LEN(str) += needed; 66 | } 67 | 68 | void php_mp_pack_bool(smart_string *str, unsigned char val) { 69 | size_t needed = mp_sizeof_bool(val); 70 | smart_string_ensure(str, needed); 71 | mp_encode_bool(SSTR_POS(str), val); 72 | SSTR_LEN(str) += needed; 73 | } 74 | 75 | void php_mp_pack_string(smart_string *str, const char *c, size_t len) { 76 | size_t needed = mp_sizeof_str(len); 77 | smart_string_ensure(str, needed); 78 | mp_encode_str(SSTR_POS(str), c, len); 79 | SSTR_LEN(str) += needed; 80 | } 81 | 82 | void php_mp_pack_hash(smart_string *str, size_t len) { 83 | size_t needed = mp_sizeof_map(len); 84 | smart_string_ensure(str, needed); 85 | mp_encode_map(SSTR_POS(str), len); 86 | SSTR_LEN(str) += needed; 87 | } 88 | 89 | void php_mp_pack_array(smart_string *str, size_t len) { 90 | size_t needed = mp_sizeof_array(len); 91 | smart_string_ensure(str, needed); 92 | mp_encode_array(SSTR_POS(str), len); 93 | SSTR_LEN(str) += needed; 94 | } 95 | 96 | int php_mp_is_hash(zval *val) { 97 | HashTable *ht = Z_ARRVAL_P(val); 98 | int count = zend_hash_num_elements(ht); 99 | if (count == 0) { 100 | /* An empty array is considered as a list. */ 101 | return 0; 102 | } else if (count != ht->nNextFreeElement) { 103 | return 1; 104 | } else { 105 | HashPosition pos = {0}; 106 | zend_hash_internal_pointer_reset_ex(ht, &pos); 107 | int i = 0; 108 | for (; i < count; ++i) { 109 | if (zend_hash_get_current_key_type_ex(ht, &pos) != \ 110 | HASH_KEY_IS_LONG) 111 | return 1; 112 | zend_hash_move_forward_ex(ht, &pos); 113 | } 114 | } 115 | return 0; 116 | } 117 | 118 | void php_mp_pack_array_recursively(smart_string *str, zval *val) { 119 | HashTable *ht = Z_ARRVAL_P(val); 120 | size_t n = zend_hash_num_elements(ht); 121 | 122 | zval *data; 123 | 124 | php_mp_pack_array(str, n); 125 | size_t key_index = 0; 126 | for (; key_index < n; ++key_index) { 127 | data = zend_hash_index_find(ht, key_index); 128 | if (!data || data == val || ARRAY_IS_RECURSIVE(data)) { 129 | php_mp_pack_nil(str); 130 | } else { 131 | ARRAY_PROTECT_RECURSION(data); 132 | php_mp_pack(str, data); 133 | ARRAY_UNPROTECT_RECURSION(data); 134 | } 135 | } 136 | } 137 | 138 | void php_mp_pack_hash_recursively(smart_string *str, zval *val) { 139 | HashTable *ht = Z_ARRVAL_P(val); 140 | size_t n = zend_hash_num_elements(ht); 141 | 142 | zend_string *key; 143 | int key_type; 144 | zend_ulong key_index; 145 | zval *data; 146 | HashPosition pos; 147 | 148 | php_mp_pack_hash(str, n); 149 | zend_hash_internal_pointer_reset_ex(ht, &pos); 150 | for (;; zend_hash_move_forward_ex(ht, &pos)) { 151 | key_type = zend_hash_get_current_key_ex(ht, &key, &key_index, &pos); 152 | if (key_type == HASH_KEY_NON_EXISTENT) 153 | break; 154 | switch (key_type) { 155 | case HASH_KEY_IS_LONG: 156 | php_mp_pack_long(str, key_index); 157 | break; 158 | case HASH_KEY_IS_STRING: 159 | php_mp_pack_string(str, ZSTR_VAL(key), ZSTR_LEN(key)); 160 | break; 161 | default: 162 | /* TODO: THROW EXCEPTION */ 163 | php_mp_pack_string(str, "", strlen("")); 164 | break; 165 | } 166 | data = zend_hash_get_current_data_ex(ht, &pos); 167 | if (!data || data == val || ARRAY_IS_RECURSIVE(data)) { 168 | php_mp_pack_nil(str); 169 | } else { 170 | ARRAY_PROTECT_RECURSION(data); 171 | php_mp_pack(str, data); 172 | ARRAY_UNPROTECT_RECURSION(data); 173 | } 174 | } 175 | } 176 | 177 | void php_mp_pack(smart_string *str, zval *val) { 178 | if (Z_TYPE_P(val) == IS_REFERENCE) 179 | val = Z_REFVAL_P(val); 180 | 181 | switch(Z_TYPE_P(val)) { 182 | case IS_NULL: 183 | php_mp_pack_nil(str); 184 | break; 185 | case IS_LONG: 186 | php_mp_pack_long(str, Z_LVAL_P(val)); 187 | break; 188 | case IS_DOUBLE: 189 | php_mp_pack_double(str, (double )Z_DVAL_P(val)); 190 | break; 191 | case IS_TRUE: 192 | case IS_FALSE: 193 | php_mp_pack_bool(str, Z_TYPE_P(val) == IS_TRUE ? 1 : 0); 194 | break; 195 | case IS_ARRAY: 196 | if (php_mp_is_hash(val)) 197 | php_mp_pack_hash_recursively(str, val); 198 | else 199 | php_mp_pack_array_recursively(str, val); 200 | break; 201 | case IS_STRING: 202 | php_mp_pack_string(str, Z_STRVAL_P(val), Z_STRLEN_P(val)); 203 | break; 204 | default: 205 | /* TODO: THROW EXCEPTION */ 206 | php_mp_pack_nil(str); 207 | break; 208 | } 209 | } 210 | 211 | /* UNPACKING ROUTINES */ 212 | 213 | ptrdiff_t php_mp_unpack_nil(zval *oval, char **str) { 214 | size_t needed = mp_sizeof_nil(); 215 | mp_decode_nil((const char **)str); 216 | ZVAL_NULL(oval); 217 | str += 1; 218 | return needed; 219 | } 220 | 221 | ptrdiff_t php_mp_unpack_uint(zval *oval, char **str) { 222 | unsigned long val = mp_decode_uint((const char **)str); 223 | ZVAL_LONG(oval, val); 224 | return mp_sizeof_uint(val); 225 | } 226 | 227 | ptrdiff_t php_mp_unpack_int(zval *oval, char **str) { 228 | long val = mp_decode_int((const char **)str); 229 | ZVAL_LONG(oval, val); 230 | return mp_sizeof_int(val); 231 | } 232 | 233 | ptrdiff_t php_mp_unpack_str(zval *oval, char **str) { 234 | uint32_t len = 0; 235 | const char *out = mp_decode_str((const char **)str, &len); 236 | ZVAL_STRINGL(oval, out, len); 237 | return mp_sizeof_str(len); 238 | } 239 | 240 | ptrdiff_t php_mp_unpack_bin(zval *oval, char **str) { 241 | uint32_t len = 0; 242 | const char *out = mp_decode_bin((const char **)str, &len); 243 | char *out_alloc = emalloc(len * sizeof(char)); 244 | memcpy(out_alloc, out, len); 245 | ZVAL_STRINGL(oval, out_alloc, len); 246 | efree(out_alloc); 247 | return mp_sizeof_bin(len); 248 | } 249 | 250 | ptrdiff_t php_mp_unpack_bool(zval *oval, char **str) { 251 | if (mp_decode_bool((const char **)str)) { 252 | ZVAL_TRUE(oval); 253 | } else { 254 | ZVAL_FALSE(oval); 255 | } 256 | return mp_sizeof_bool(str); 257 | } 258 | 259 | ptrdiff_t php_mp_unpack_float(zval *oval, char **str) { 260 | float val = mp_decode_float((const char **)str); 261 | ZVAL_DOUBLE(oval, (double )val); 262 | return mp_sizeof_float(val); 263 | } 264 | 265 | ptrdiff_t php_mp_unpack_double(zval *oval, char **str) { 266 | double val = mp_decode_double((const char **)str); 267 | ZVAL_DOUBLE(oval, (double )val); 268 | return mp_sizeof_double(val); 269 | } 270 | 271 | ptrdiff_t php_mp_unpack_map(zval *oval, char **str) { 272 | TSRMLS_FETCH(); 273 | size_t len = mp_decode_map((const char **)str); 274 | array_init_size(oval, len); 275 | while (len-- > 0) { 276 | zval key = {0}, value = {0}; 277 | ZVAL_UNDEF(&key); 278 | ZVAL_UNDEF(&value); 279 | if (php_mp_unpack(&key, str) == FAILURE) { 280 | goto error_key; 281 | } 282 | if (php_mp_unpack(&value, str) == FAILURE) { 283 | goto error_value; 284 | } 285 | switch (Z_TYPE(key)) { 286 | case IS_LONG: 287 | add_index_zval(oval, Z_LVAL(key), &value); 288 | break; 289 | case IS_STRING: 290 | add_assoc_zval(oval, Z_STRVAL(key), &value); 291 | break; 292 | case IS_DOUBLE: 293 | /* convert to INT/STRING for future uses */ 294 | /* FALLTHROUGH */ 295 | default: { 296 | THROW_EXC("Bad key type for PHP Array"); 297 | goto error; 298 | } 299 | } 300 | zval_ptr_dtor(&key); 301 | continue; 302 | error: 303 | zval_ptr_dtor(&value); 304 | error_value: 305 | zval_ptr_dtor(&key); 306 | error_key: 307 | zval_ptr_dtor(oval); 308 | return FAILURE; 309 | } 310 | return SUCCESS; 311 | } 312 | 313 | ptrdiff_t php_mp_unpack_array(zval *oval, char **str) { 314 | size_t len = mp_decode_array((const char **)str); 315 | array_init_size(oval, len); 316 | while (len-- > 0) { 317 | zval value; 318 | if (php_mp_unpack(&value, str) == FAILURE) { 319 | zval_ptr_dtor(oval); 320 | return FAILURE; 321 | } 322 | add_next_index_zval(oval, &value); 323 | } 324 | return SUCCESS; 325 | } 326 | 327 | ssize_t php_mp_unpack(zval *oval, char **str) { 328 | size_t needed = 0; 329 | switch (mp_typeof(**str)) { 330 | case MP_NIL: 331 | return php_mp_unpack_nil(oval, str); 332 | case MP_UINT: 333 | return php_mp_unpack_uint(oval, str); 334 | case MP_INT: 335 | return php_mp_unpack_int(oval, str); 336 | case MP_STR: 337 | return php_mp_unpack_str(oval, str); 338 | case MP_BIN: 339 | return php_mp_unpack_bin(oval, str); 340 | case MP_ARRAY: 341 | return php_mp_unpack_array(oval, str); 342 | case MP_MAP: 343 | return php_mp_unpack_map(oval, str); 344 | case MP_BOOL: 345 | return php_mp_unpack_bool(oval, str); 346 | case MP_FLOAT: 347 | return php_mp_unpack_float(oval, str); 348 | case MP_DOUBLE: 349 | return php_mp_unpack_double(oval, str); 350 | case MP_EXT: 351 | break; 352 | } 353 | return FAILURE; 354 | } 355 | 356 | /* SIZEOF ROUTINES */ 357 | size_t php_mp_sizeof_nil() { 358 | return mp_sizeof_nil(); 359 | } 360 | 361 | size_t php_mp_sizeof_long_pos(long val) { 362 | return mp_sizeof_uint(val); 363 | } 364 | 365 | size_t php_mp_sizeof_long_neg(long val) { 366 | return mp_sizeof_int(val); 367 | } 368 | 369 | size_t php_mp_sizeof_long(long val) { 370 | if (val >= 0) 371 | return php_mp_sizeof_long_pos(val); 372 | return php_mp_sizeof_long_neg(val); 373 | } 374 | 375 | size_t php_mp_sizeof_double(double val) { 376 | return mp_sizeof_double(val); 377 | } 378 | 379 | size_t php_mp_sizeof_bool(unsigned char val) { 380 | return mp_sizeof_bool(val); 381 | } 382 | 383 | size_t php_mp_sizeof_string(size_t len) { 384 | return mp_sizeof_str(len); 385 | } 386 | 387 | size_t php_mp_sizeof_hash(size_t len) { 388 | return mp_sizeof_map(len); 389 | } 390 | 391 | size_t php_mp_sizeof_array(size_t len) { 392 | return mp_sizeof_array(len); 393 | } 394 | 395 | size_t php_mp_sizeof_array_recursively(zval *val) { 396 | TSRMLS_FETCH(); 397 | HashTable *ht = HASH_OF(val); 398 | size_t n = zend_hash_num_elements(ht); 399 | size_t needed = php_mp_sizeof_array(n); 400 | size_t key_index = 0; 401 | zval *data; 402 | 403 | for (; key_index < n; ++key_index) { 404 | data = zend_hash_index_find(ht, key_index); 405 | if (!data || data == val || ARRAY_IS_RECURSIVE(data)) { 406 | needed += php_mp_sizeof_nil(); 407 | } else { 408 | ARRAY_PROTECT_RECURSION(data); 409 | needed += php_mp_sizeof(data); 410 | ARRAY_UNPROTECT_RECURSION(data); 411 | } 412 | } 413 | return needed; 414 | } 415 | 416 | size_t php_mp_sizeof_hash_recursively(zval *val) { 417 | TSRMLS_FETCH(); 418 | HashTable *ht = HASH_OF(val); 419 | size_t n = zend_hash_num_elements(ht); 420 | size_t needed = php_mp_sizeof_hash(n); 421 | 422 | zend_string *key; 423 | int key_type; 424 | zend_ulong key_index; 425 | zval *data; 426 | HashPosition pos; 427 | 428 | zend_hash_internal_pointer_reset_ex(ht, &pos); 429 | for (;; zend_hash_move_forward_ex(ht, &pos)) { 430 | key_type = zend_hash_get_current_key_ex(ht, &key, &key_index, &pos); 431 | if (key_type == HASH_KEY_NON_EXISTENT) 432 | break; 433 | switch (key_type) { 434 | case HASH_KEY_IS_LONG: 435 | needed += php_mp_sizeof_long(key_index); 436 | break; 437 | case HASH_KEY_IS_STRING: 438 | needed += php_mp_sizeof_string(ZSTR_LEN(key)); 439 | break; 440 | default: 441 | /* TODO: THROW EXCEPTION */ 442 | needed += php_mp_sizeof_string(strlen("")); 443 | break; 444 | } 445 | data = zend_hash_get_current_data_ex(ht, &pos); 446 | if (!data || data == val || ARRAY_IS_RECURSIVE(data)) { 447 | needed += php_mp_sizeof_nil(); 448 | } else { 449 | ARRAY_PROTECT_RECURSION(data); 450 | needed += php_mp_sizeof(data); 451 | ARRAY_UNPROTECT_RECURSION(data); 452 | } 453 | } 454 | return needed; 455 | } 456 | 457 | 458 | size_t php_mp_sizeof(zval *val) { 459 | if (Z_TYPE_P(val) == IS_REFERENCE) 460 | val = Z_REFVAL_P(val); 461 | 462 | switch(Z_TYPE_P(val)) { 463 | case IS_NULL: 464 | return php_mp_sizeof_nil(); 465 | break; 466 | case IS_LONG: 467 | return php_mp_sizeof_long(Z_LVAL_P(val)); 468 | break; 469 | case IS_DOUBLE: 470 | return php_mp_sizeof_double((double )Z_DVAL_P(val)); 471 | break; 472 | case IS_TRUE: 473 | case IS_FALSE: 474 | return php_mp_sizeof_bool(Z_TYPE_P(val) == IS_TRUE ? 1 : 0); 475 | break; 476 | case IS_ARRAY: 477 | if (php_mp_is_hash(val)) 478 | return php_mp_sizeof_hash_recursively(val); 479 | return php_mp_sizeof_array_recursively(val); 480 | break; 481 | case IS_STRING: 482 | return php_mp_sizeof_string(Z_STRLEN_P(val)); 483 | break; 484 | default: 485 | /* TODO: THROW EXCEPTION */ 486 | return php_mp_sizeof_nil(); 487 | break; 488 | } 489 | } 490 | 491 | /* OTHER */ 492 | 493 | size_t php_mp_check(const char *str, size_t str_size) { 494 | return mp_check(&str, str + str_size); 495 | } 496 | 497 | void php_mp_pack_package_size_basic(char *pos, size_t val) { 498 | *pos = 0xce; 499 | *(uint32_t *)(pos + 1) = mp_bswap_u32(val); 500 | } 501 | 502 | void php_mp_pack_package_size(smart_string *str, size_t val) { 503 | size_t needed = 5; 504 | smart_string_ensure(str, needed); 505 | php_mp_pack_package_size_basic(SSTR_POS(str), val); 506 | SSTR_LEN(str) += needed; 507 | } 508 | 509 | size_t php_mp_unpack_package_size(char* str) { 510 | return mp_decode_uint((const char **)&str); 511 | } 512 | -------------------------------------------------------------------------------- /src/tarantool_msgpack.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_MSGPACK_H 2 | #define PHP_TNT_MSGPACK_H 3 | 4 | #include "php_tarantool.h" 5 | 6 | #define PHP_MP_SERIALIZABLE_P(v) (Z_TYPE_P(v) == IS_NULL || \ 7 | Z_TYPE_P(v) == IS_LONG || \ 8 | Z_TYPE_P(v) == IS_DOUBLE || \ 9 | Z_TYPE_P(v) == IS_FALSE || \ 10 | Z_TYPE_P(v) == IS_TRUE || \ 11 | Z_TYPE_P(v) == IS_ARRAY || \ 12 | Z_TYPE_P(v) == IS_STRING || \ 13 | Z_TYPE_P(v) == IS_REFERENCE) 14 | 15 | size_t php_mp_check (const char *str, size_t str_size); 16 | void php_mp_pack (smart_string *buf, zval *val ); 17 | ssize_t php_mp_unpack (zval *oval, char **str ); 18 | size_t php_mp_sizeof (zval *val); 19 | 20 | void php_mp_pack_package_size (smart_string *str, size_t val); 21 | void php_mp_pack_package_size_basic (char *pos, size_t val); 22 | size_t php_mp_unpack_package_size (char *buf); 23 | 24 | int php_mp_is_hash(zval *val); 25 | 26 | void php_mp_pack_nil(smart_string *str); 27 | void php_mp_pack_long_pos(smart_string *str, long val); 28 | void php_mp_pack_long_neg(smart_string *str, long val); 29 | void php_mp_pack_long(smart_string *str, long val); 30 | void php_mp_pack_double(smart_string *str, double val); 31 | void php_mp_pack_bool(smart_string *str, unsigned char val); 32 | void php_mp_pack_string(smart_string *str, const char *c, size_t len); 33 | void php_mp_pack_hash(smart_string *str, size_t len); 34 | void php_mp_pack_array(smart_string *str, size_t len); 35 | void php_mp_pack_array_recursively(smart_string *str, zval *val); 36 | void php_mp_pack_hash_recursively(smart_string *str, zval *val); 37 | 38 | size_t php_mp_sizeof_nil(); 39 | size_t php_mp_sizeof_long_pos(long val); 40 | size_t php_mp_sizeof_long_neg(long val); 41 | size_t php_mp_sizeof_long(long val); 42 | size_t php_mp_sizeof_double(double val); 43 | size_t php_mp_sizeof_bool(unsigned char val); 44 | size_t php_mp_sizeof_string(size_t len); 45 | size_t php_mp_sizeof_hash(size_t len); 46 | size_t php_mp_sizeof_array(size_t len); 47 | size_t php_mp_sizeof_array_recursively(zval *val); 48 | size_t php_mp_sizeof_hash_recursively(zval *val); 49 | 50 | int smart_string_ensure(smart_string *str, size_t len); 51 | void smart_string_nullify(smart_string *str); 52 | #endif /* PHP_TNT_MSGPACK_H */ 53 | -------------------------------------------------------------------------------- /src/tarantool_network.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "php_tarantool.h" 12 | #include "tarantool_network.h" 13 | 14 | void double_to_tv(double tm, struct timeval *tv) { 15 | tv->tv_sec = floor(tm); 16 | tv->tv_usec = floor((tm - floor(tm)) * pow(10, 6)); 17 | } 18 | 19 | void double_to_ts(double tm, struct timespec *ts) { 20 | ts->tv_sec = floor(tm); 21 | ts->tv_nsec = floor((tm - floor(tm)) * pow(10, 9)); 22 | } 23 | 24 | /* `pid` means persistent_id */ 25 | // void tntll_stream_close(php_stream *stream, const char *pid) { 26 | void tntll_stream_close(php_stream *stream, zend_string *pid) { 27 | int rv = PHP_STREAM_PERSISTENT_SUCCESS; 28 | if (stream == NULL) { 29 | rv = tntll_stream_fpid2(pid, &stream); 30 | } 31 | if (rv == PHP_STREAM_PERSISTENT_SUCCESS && stream != NULL) { 32 | if (pid) { 33 | php_stream_free(stream, PHP_STREAM_FREE_CLOSE_PERSISTENT); 34 | } else { 35 | php_stream_close(stream); 36 | } 37 | } 38 | } 39 | 40 | int tntll_stream_fpid2(zend_string *pid, php_stream **ostream) { 41 | TSRMLS_FETCH(); 42 | return php_stream_from_persistent_id(pid->val, ostream TSRMLS_CC); 43 | } 44 | 45 | int tntll_stream_fpid(const char *host, int port, zend_string *pid, 46 | php_stream **ostream, char **err) { 47 | TSRMLS_FETCH(); 48 | *ostream = NULL; 49 | int rv = 0; 50 | if (pid) { 51 | rv = php_stream_from_persistent_id(pid->val, ostream TSRMLS_CC); 52 | if (rv == PHP_STREAM_PERSISTENT_FAILURE) { 53 | spprintf(err, 0, "Failed to load persistent stream."); 54 | return -1; 55 | } 56 | } 57 | if (rv == PHP_STREAM_PERSISTENT_NOT_EXIST) { 58 | return tntll_stream_open(host, port, pid, ostream, err); 59 | } 60 | return 0; 61 | } 62 | 63 | int tntll_stream_open(const char *host, int port, zend_string *pid, 64 | php_stream **ostream, char **err) { 65 | TSRMLS_FETCH(); 66 | php_stream *stream = NULL; 67 | struct timeval tv = {0}; 68 | int errcode = 0, options = 0, flags = 0; 69 | char *addr = NULL; 70 | zend_string *errstr = NULL; 71 | size_t addr_len = 0; 72 | 73 | addr_len = spprintf(&addr, 0, "tcp://%s:%d", host, port); 74 | options = REPORT_ERRORS; 75 | if (pid) options |= STREAM_OPEN_PERSISTENT; 76 | flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; 77 | double_to_tv(TARANTOOL_G(timeout), &tv); 78 | // printf("timeout: 'sec(%d), usec(%d)'\n", 79 | // (int )tv.tv_sec, (int )tv.tv_usec); 80 | 81 | const char *pid_str = pid == NULL ? NULL : pid->val; 82 | stream = php_stream_xport_create(addr, addr_len, options, flags, 83 | pid_str, &tv, NULL, &errstr, 84 | &errcode); 85 | efree(addr); 86 | 87 | if (errcode || !stream) { 88 | spprintf(err, 0, "Failed to connect [%d]: %s", errcode, 89 | ZSTR_VAL(errstr)); 90 | goto error; 91 | } 92 | 93 | /* Set READ_TIMEOUT */ 94 | double_to_tv(TARANTOOL_G(request_timeout), &tv); 95 | // printf("request_timeout: 'sec(%d), usec(%d)'\n", 96 | // (int )tv.tv_sec, (int )tv.tv_usec); 97 | 98 | if (tv.tv_sec != 0 || tv.tv_usec != 0) { 99 | php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 100 | 0, &tv); 101 | } 102 | 103 | /* Set TCP_NODELAY */ 104 | int socketd = ((php_netstream_data_t* )stream->abstract)->socket; 105 | flags = 1; 106 | if (setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flags, 107 | sizeof(int))) { 108 | spprintf(err, 0, "Failed setsockopt [%d]: %s", errno, 109 | strerror(errno)); 110 | goto error; 111 | } 112 | *ostream = stream; 113 | return 0; 114 | error: 115 | if (errstr) zend_string_release(errstr); 116 | if (stream) tntll_stream_close(NULL, pid); 117 | return -1; 118 | } 119 | 120 | #include 121 | 122 | /* 123 | * Legacy rtsisyk code, php_stream_read made right 124 | * See https://bugs.launchpad.net/tarantool/+bug/1182474 125 | */ 126 | size_t tntll_stream_read2(php_stream *stream, char *buf, size_t size) { 127 | TSRMLS_FETCH(); 128 | size_t total_size = 0; 129 | ssize_t read_size = 0; 130 | 131 | while (total_size < size) { 132 | read_size = php_stream_read(stream, buf + total_size, 133 | size - total_size); 134 | assert(read_size + total_size <= size); 135 | if (read_size <= 0) 136 | break; 137 | total_size += read_size; 138 | } 139 | 140 | return total_size; 141 | } 142 | 143 | bool tntll_stream_is_timedout() { 144 | int err = php_socket_errno(); 145 | if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS) 146 | return 1; 147 | return 0; 148 | } 149 | 150 | int tntll_stream_read(php_stream *stream, char *buf, size_t size) { 151 | TSRMLS_FETCH(); 152 | return php_stream_read(stream, buf, size); 153 | } 154 | 155 | int tntll_stream_send(php_stream *stream, char *buf, size_t size) { 156 | TSRMLS_FETCH(); 157 | if (php_stream_write(stream, buf, size) != size || 158 | php_stream_flush(stream)) 159 | return -1; 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /src/tarantool_network.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_NETWORK_H 2 | #define PHP_TNT_NETWORK_H 3 | 4 | struct timeval; 5 | struct timespec; 6 | 7 | void double_to_tv(double tm, struct timeval *tv); 8 | void double_to_ts(double tm, struct timespec *ts); 9 | 10 | void tntll_stream_close(php_stream *stream, zend_string *pid); 11 | /* 12 | * Simple php_stream_from_persistent_id 13 | */ 14 | int tntll_stream_fpid2(zend_string *pid, php_stream **ostream); 15 | /* 16 | * php_stream_from_persistent_id if persistent. 17 | * If exists or if not persistent, then open new connection. 18 | */ 19 | int tntll_stream_fpid (const char *host, int port, zend_string *pid, 20 | php_stream **ostream, char **err); 21 | int tntll_stream_open (const char *host, int port, zend_string *pid, 22 | php_stream **ostream, char **err); 23 | /* 24 | * Read size bytes once 25 | */ 26 | int tntll_stream_read (php_stream *stream, char *buf, size_t size); 27 | /* 28 | * Read size bytes exactly (if not error) 29 | */ 30 | size_t tntll_stream_read2(php_stream *stream, char *buf, size_t size); 31 | int tntll_stream_send (php_stream *stream, char *buf, size_t size); 32 | 33 | bool tntll_stream_is_timedout(); 34 | 35 | #endif /* PHP_TNT_NETWORK_H */ 36 | -------------------------------------------------------------------------------- /src/tarantool_proto.c: -------------------------------------------------------------------------------- 1 | #include "php_tarantool.h" 2 | 3 | #include "tarantool_tp.h" 4 | #include "tarantool_proto.h" 5 | #include "tarantool_msgpack.h" 6 | 7 | #include "third_party/msgpuck.h" 8 | 9 | static size_t php_tp_sizeof_header(uint32_t request, uint32_t sync) { 10 | return php_mp_sizeof_hash(2) + 11 | php_mp_sizeof_long(TNT_CODE) + 12 | php_mp_sizeof_long(request) + 13 | php_mp_sizeof_long(TNT_SYNC) + 14 | php_mp_sizeof_long(sync) ; 15 | } 16 | 17 | static char *php_tp_pack_header(smart_string *str, size_t size, 18 | uint32_t request, uint32_t sync) { 19 | char *sz = SSTR_POS(str); 20 | php_mp_pack_package_size(str, size); 21 | php_mp_pack_hash(str, 2); 22 | php_mp_pack_long(str, TNT_CODE); 23 | php_mp_pack_long(str, request); 24 | php_mp_pack_long(str, TNT_SYNC); 25 | php_mp_pack_long(str, sync); 26 | return sz; 27 | } 28 | 29 | size_t php_tp_sizeof_auth(uint32_t sync, size_t ulen, zend_bool nopass) { 30 | size_t val = php_tp_sizeof_header(TNT_AUTH, sync) + 31 | php_mp_sizeof_hash(2) + 32 | php_mp_sizeof_long(TNT_USERNAME) + 33 | php_mp_sizeof_string(ulen) + 34 | php_mp_sizeof_long(TNT_TUPLE) + 35 | php_mp_sizeof_array((nopass ? 0 : 2)); 36 | if (!nopass) { 37 | val += php_mp_sizeof_string(9) + 38 | php_mp_sizeof_string(SCRAMBLE_SIZE) ; 39 | } 40 | return val; 41 | } 42 | 43 | void php_tp_encode_auth( 44 | smart_string *str, 45 | uint32_t sync, 46 | char * const username, 47 | size_t username_len, 48 | char * const scramble) { 49 | zend_bool nopass = (zend_bool )(scramble != NULL); 50 | size_t packet_size = php_tp_sizeof_auth(sync, username_len, nopass); 51 | smart_string_ensure(str, packet_size + 5); 52 | php_tp_pack_header(str, packet_size, TNT_AUTH, sync); 53 | 54 | php_mp_pack_hash(str, 2); 55 | php_mp_pack_long(str, TNT_USERNAME); 56 | php_mp_pack_string(str, username, username_len); 57 | php_mp_pack_long(str, TNT_TUPLE); 58 | php_mp_pack_array(str, (nopass ? 0 : 2)); 59 | if (!nopass) { 60 | php_mp_pack_string(str, "chap-sha1", 9); 61 | php_mp_pack_string(str, scramble, SCRAMBLE_SIZE); 62 | } 63 | } 64 | 65 | size_t php_tp_sizeof_ping(uint32_t sync) { 66 | return php_tp_sizeof_header(TNT_PING, sync); 67 | } 68 | 69 | void php_tp_encode_ping( 70 | smart_string *str, 71 | uint32_t sync) { 72 | size_t packet_size = php_tp_sizeof_ping(sync); 73 | smart_string_ensure(str, packet_size + 5); 74 | php_tp_pack_header(str, packet_size, TNT_PING, sync); 75 | } 76 | 77 | size_t php_tp_sizeof_select(uint32_t sync, uint32_t space_no, 78 | uint32_t index_no, uint32_t limit, 79 | uint32_t offset, uint32_t iterator, 80 | zval *key) { 81 | return php_tp_sizeof_header(TNT_SELECT, sync) + 82 | php_mp_sizeof_hash(6) + 83 | php_mp_sizeof_long(TNT_SPACE) + 84 | php_mp_sizeof_long(space_no) + 85 | php_mp_sizeof_long(TNT_INDEX) + 86 | php_mp_sizeof_long(index_no) + 87 | php_mp_sizeof_long(TNT_OFFSET) + 88 | php_mp_sizeof_long(offset) + 89 | php_mp_sizeof_long(TNT_LIMIT) + 90 | php_mp_sizeof_long(limit) + 91 | php_mp_sizeof_long(TNT_ITERATOR) + 92 | php_mp_sizeof_long(iterator) + 93 | php_mp_sizeof_long(TNT_KEY) + 94 | php_mp_sizeof(key) ; 95 | } 96 | 97 | void php_tp_encode_select(smart_string *str, 98 | uint32_t sync, uint32_t space_no, 99 | uint32_t index_no, uint32_t limit, 100 | uint32_t offset, uint32_t iterator, 101 | zval *key) { 102 | size_t packet_size = php_tp_sizeof_select(sync, 103 | space_no, index_no, offset, limit, iterator, key); 104 | 105 | smart_string_ensure(str, packet_size + 5); 106 | php_tp_pack_header(str, packet_size, TNT_SELECT, sync); 107 | php_mp_pack_hash(str, 6); 108 | php_mp_pack_long(str, TNT_SPACE); 109 | php_mp_pack_long(str, space_no); 110 | php_mp_pack_long(str, TNT_INDEX); 111 | php_mp_pack_long(str, index_no); 112 | php_mp_pack_long(str, TNT_OFFSET); 113 | php_mp_pack_long(str, offset); 114 | php_mp_pack_long(str, TNT_LIMIT); 115 | php_mp_pack_long(str, limit); 116 | php_mp_pack_long(str, TNT_ITERATOR); 117 | php_mp_pack_long(str, iterator); 118 | php_mp_pack_long(str, TNT_KEY); 119 | php_mp_pack(str, key); 120 | } 121 | 122 | size_t php_tp_sizeof_insert_or_replace(uint32_t sync, 123 | uint32_t space_no, zval *tuple, uint32_t type) { 124 | return php_tp_sizeof_header(type, sync) + 125 | php_mp_sizeof_hash(2) + 126 | php_mp_sizeof_long(TNT_SPACE) + 127 | php_mp_sizeof_long(space_no) + 128 | php_mp_sizeof_long(TNT_TUPLE) + 129 | php_mp_sizeof(tuple) ; 130 | } 131 | 132 | void php_tp_encode_insert_or_replace(smart_string *str, uint32_t sync, 133 | uint32_t space_no, zval *tuple, uint32_t type) { 134 | assert(type == TNT_INSERT || type == TNT_REPLACE); 135 | size_t packet_size = php_tp_sizeof_insert_or_replace(sync, 136 | space_no, tuple, type); 137 | smart_string_ensure(str, packet_size + 5); 138 | php_tp_pack_header(str, packet_size, type, sync); 139 | php_mp_pack_hash(str, 2); 140 | php_mp_pack_long(str, TNT_SPACE); 141 | php_mp_pack_long(str, space_no); 142 | php_mp_pack_long(str, TNT_TUPLE); 143 | php_mp_pack(str, tuple); 144 | } 145 | 146 | size_t php_tp_sizeof_delete(uint32_t sync, 147 | uint32_t space_no, uint32_t index_no, 148 | zval *tuple) { 149 | return php_tp_sizeof_header(TNT_DELETE, sync) + 150 | php_mp_sizeof_hash(3) + 151 | php_mp_sizeof_long(TNT_SPACE) + 152 | php_mp_sizeof_long(space_no) + 153 | php_mp_sizeof_long(TNT_INDEX) + 154 | php_mp_sizeof_long(index_no) + 155 | php_mp_sizeof_long(TNT_KEY) + 156 | php_mp_sizeof(tuple) ; 157 | } 158 | 159 | void php_tp_encode_delete(smart_string *str, uint32_t sync, 160 | uint32_t space_no, uint32_t index_no, 161 | zval *tuple) { 162 | size_t packet_size = php_tp_sizeof_delete(sync, 163 | space_no, index_no, tuple); 164 | smart_string_ensure(str, packet_size + 5); 165 | php_tp_pack_header(str, packet_size, TNT_DELETE, sync); 166 | php_mp_pack_hash(str, 3); 167 | php_mp_pack_long(str, TNT_SPACE); 168 | php_mp_pack_long(str, space_no); 169 | php_mp_pack_long(str, TNT_INDEX); 170 | php_mp_pack_long(str, index_no); 171 | php_mp_pack_long(str, TNT_KEY); 172 | php_mp_pack(str, tuple); 173 | } 174 | 175 | static size_t php_tp_sizeof_call(uint32_t sync, uint32_t proc_len, 176 | enum tnt_request_type type, zval *tuple) { 177 | return php_tp_sizeof_header(type, sync) + 178 | php_mp_sizeof_hash(2) + 179 | php_mp_sizeof_long(TNT_FUNCTION) + 180 | php_mp_sizeof_string(proc_len) + 181 | php_mp_sizeof_long(TNT_TUPLE) + 182 | php_mp_sizeof(tuple) ; 183 | } 184 | 185 | static void php_tp_encode_call_impl(smart_string *str, uint32_t sync, 186 | char *proc, uint32_t proc_len, 187 | enum tnt_request_type type, zval *tuple) { 188 | size_t packet_size = php_tp_sizeof_call(sync, proc_len, type, tuple); 189 | smart_string_ensure(str, packet_size + 5); 190 | php_tp_pack_header(str, packet_size, type, sync); 191 | php_mp_pack_hash(str, 2); 192 | php_mp_pack_long(str, TNT_FUNCTION); 193 | php_mp_pack_string(str, proc, proc_len); 194 | php_mp_pack_long(str, TNT_TUPLE); 195 | php_mp_pack(str, tuple); 196 | } 197 | 198 | void php_tp_encode_call(smart_string *str, uint32_t sync, char *proc, 199 | uint32_t proc_len, zval *tuple) { 200 | php_tp_encode_call_impl(str,sync, proc, proc_len, TNT_CALL, tuple); 201 | } 202 | 203 | void php_tp_encode_call_16(smart_string *str, uint32_t sync, char *proc, 204 | uint32_t proc_len, zval *tuple) { 205 | php_tp_encode_call_impl(str,sync, proc, proc_len, TNT_CALL_16, tuple); 206 | } 207 | 208 | size_t php_tp_sizeof_eval(uint32_t sync, 209 | uint32_t proc_len, zval *tuple) { 210 | return php_tp_sizeof_header(TNT_EVAL, sync) + 211 | php_mp_sizeof_hash(2) + 212 | php_mp_sizeof_long(TNT_EXPRESSION) + 213 | php_mp_sizeof_string(proc_len) + 214 | php_mp_sizeof_long(TNT_TUPLE) + 215 | php_mp_sizeof(tuple) ; 216 | } 217 | 218 | void php_tp_encode_eval(smart_string *str, uint32_t sync, 219 | char *proc, uint32_t proc_len, zval *tuple) { 220 | size_t packet_size = php_tp_sizeof_eval(sync, 221 | proc_len, tuple); 222 | smart_string_ensure (str, packet_size + 5); 223 | php_tp_pack_header (str, packet_size, TNT_EVAL, sync); 224 | php_mp_pack_hash (str, 2); 225 | php_mp_pack_long (str, TNT_EXPRESSION); 226 | php_mp_pack_string (str, proc, proc_len); 227 | php_mp_pack_long (str, TNT_TUPLE); 228 | php_mp_pack (str, tuple); 229 | } 230 | 231 | char *php_tp_encode_update(smart_string *str, uint32_t sync, 232 | uint32_t space_no, uint32_t index_no, 233 | zval *key) { 234 | char *sz = php_tp_pack_header(str, 0, TNT_UPDATE, sync); 235 | php_mp_pack_hash (str, 4); 236 | php_mp_pack_long (str, TNT_SPACE); 237 | php_mp_pack_long (str, space_no); 238 | php_mp_pack_long (str, TNT_INDEX); 239 | php_mp_pack_long (str, index_no); 240 | php_mp_pack_long (str, TNT_KEY); 241 | php_mp_pack (str, key); 242 | php_mp_pack_long (str, TNT_TUPLE); 243 | return sz; 244 | } 245 | 246 | char *php_tp_encode_upsert(smart_string *str, uint32_t sync, 247 | uint32_t space_no, zval *tuple) { 248 | char *sz = php_tp_pack_header(str, 0, TNT_UPSERT, sync); 249 | php_mp_pack_hash (str, 3); 250 | php_mp_pack_long (str, TNT_SPACE); 251 | php_mp_pack_long (str, space_no); 252 | php_mp_pack_long (str, TNT_TUPLE); 253 | php_mp_pack (str, tuple); 254 | php_mp_pack_long (str, TNT_OPS); 255 | return sz; 256 | } 257 | 258 | void php_tp_encode_uheader(smart_string *str, size_t op_count) { 259 | php_mp_pack_array(str, op_count); 260 | } 261 | 262 | void php_tp_encode_uother(smart_string *str, char type, uint32_t fieldno, 263 | zval *value) { 264 | php_mp_pack_array(str, 3); 265 | php_mp_pack_string(str, (const char *)&type, 1); 266 | php_mp_pack_long(str, fieldno); 267 | php_mp_pack(str, value); 268 | } 269 | 270 | void php_tp_encode_usplice(smart_string *str, uint32_t fieldno, 271 | uint32_t position, uint32_t offset, 272 | const char *buffer, size_t buffer_len) { 273 | php_mp_pack_array(str, 5); 274 | php_mp_pack_string(str, ":", 1); 275 | php_mp_pack_long(str, fieldno); 276 | php_mp_pack_long(str, position); 277 | php_mp_pack_long(str, offset); 278 | php_mp_pack_string(str, buffer, buffer_len); 279 | } 280 | 281 | void php_tp_reencode_length(smart_string *str, size_t orig_len) { 282 | ssize_t package_size = (SSTR_LEN(str) - orig_len) - 5; 283 | assert(package_size > 0); 284 | char *sz = SSTR_BEG(str) + orig_len; 285 | php_mp_pack_package_size_basic(sz, package_size); 286 | } 287 | 288 | int64_t php_tp_response(struct tnt_response *r, char *buf, size_t size) 289 | { 290 | memset(r, 0, sizeof(*r)); 291 | const char *p = buf; 292 | /* len */ 293 | uint32_t len = size; 294 | /* header */ 295 | if (mp_typeof(*p) != MP_MAP) 296 | return -1; 297 | uint32_t n = mp_decode_map(&p); 298 | while (n-- > 0) { 299 | if (mp_typeof(*p) != MP_UINT) 300 | return -1; 301 | uint32_t key = mp_decode_uint(&p); 302 | if (mp_typeof(*p) != MP_UINT) 303 | return -1; 304 | switch (key) { 305 | case TNT_SYNC: 306 | r->sync = mp_decode_uint(&p); 307 | break; 308 | case TNT_CODE: 309 | r->code = mp_decode_uint(&p); 310 | break; 311 | default: 312 | mp_next(&p); 313 | } 314 | r->bitmap |= (1ULL << key); 315 | } 316 | /* body */ 317 | if (mp_typeof(*p) != MP_MAP) 318 | return -1; 319 | n = mp_decode_map(&p); 320 | while (n-- > 0) { 321 | uint32_t key = mp_decode_uint(&p); 322 | switch (key) { 323 | case TNT_ERROR: 324 | if (mp_typeof(*p) != MP_STR) 325 | return -1; 326 | uint32_t elen = 0; 327 | r->error = mp_decode_str(&p, &elen); 328 | r->error_len = elen; 329 | r->code &= ((1 << 15) - 1); 330 | break; 331 | case TNT_DATA: 332 | if (mp_typeof(*p) != MP_ARRAY) 333 | return -1; 334 | r->data = p; 335 | mp_next(&p); 336 | r->data_len = p - r->data; 337 | break; 338 | default: 339 | mp_next(&p); 340 | } 341 | r->bitmap |= (1ULL << key); 342 | } 343 | return p - buf; 344 | } 345 | 346 | int convert_iter_str(const char *i, size_t i_len) { 347 | int first = toupper(i[0]); 348 | switch (first) { 349 | case 'A': 350 | if (i_len == 3 && toupper(i[1]) == 'L' && toupper(i[2]) == 'L') 351 | return ITERATOR_ALL; 352 | break; 353 | case 'B': 354 | if (i_len > 7 && toupper(i[1]) == 'I' && 355 | toupper(i[2]) == 'T' && toupper(i[3]) == 'S' && 356 | toupper(i[4]) == 'E' && toupper(i[5]) == 'T' && 357 | toupper(i[6]) == '_') { 358 | if (i_len == 18 && toupper(i[7]) == 'A' && 359 | toupper(i[8]) == 'L' && toupper(i[9]) == 'L' && 360 | toupper(i[10]) == '_' && toupper(i[11]) == 'N' && 361 | toupper(i[12]) == 'O' && toupper(i[13]) == 'T' && 362 | toupper(i[14]) == '_' && toupper(i[15]) == 'S' && 363 | toupper(i[16]) == 'E' && toupper(i[17]) == 'T') 364 | return ITERATOR_BITSET_ALL_NOT_SET; 365 | else if (i_len == 14 && toupper(i[7]) == 'A' && 366 | toupper(i[8]) == 'L' && toupper(i[9]) == 'L' && 367 | toupper(i[10]) == '_' && toupper(i[11]) == 'S' && 368 | toupper(i[12]) == 'E' && toupper(i[13]) == 'T') 369 | return ITERATOR_BITSET_ALL_SET; 370 | else if (i_len == 14 && toupper(i[7]) == 'A' && 371 | toupper(i[8]) == 'N' && toupper(i[9]) == 'Y' && 372 | toupper(i[10]) == '_' && toupper(i[11]) == 'S' && 373 | toupper(i[12]) == 'E' && toupper(i[13]) == 'T') 374 | return ITERATOR_BITSET_ANY_SET; 375 | } 376 | if (i_len > 4 && toupper(i[1]) == 'I' && 377 | toupper(i[2]) == 'T' && toupper(i[3]) == 'S' && 378 | toupper(i[4]) == '_') { 379 | if (i_len == 16 && toupper(i[5]) == 'A' && 380 | toupper(i[6]) == 'L' && toupper(i[7]) == 'L' && 381 | toupper(i[8]) == '_' && toupper(i[9]) == 'N' && 382 | toupper(i[10]) == 'O' && toupper(i[11]) == 'T' && 383 | toupper(i[12]) == '_' && toupper(i[13]) == 'S' && 384 | toupper(i[14]) == 'E' && toupper(i[15]) == 'T') 385 | return ITERATOR_BITSET_ALL_NOT_SET; 386 | else if (i_len == 12 && toupper(i[5]) == 'A' && 387 | toupper(i[6]) == 'L' && toupper(i[7]) == 'L' && 388 | toupper(i[8]) == '_' && toupper(i[9]) == 'S' && 389 | toupper(i[10]) == 'E' && toupper(i[11]) == 'T') 390 | return ITERATOR_BITSET_ALL_SET; 391 | else if (i_len == 12 && toupper(i[5]) == 'A' && 392 | toupper(i[6]) == 'N' && toupper(i[7]) == 'Y' && 393 | toupper(i[8]) == '_' && toupper(i[9]) == 'S' && 394 | toupper(i[10]) == 'E' && toupper(i[11]) == 'T') 395 | return ITERATOR_BITSET_ANY_SET; 396 | } 397 | break; 398 | case 'E': 399 | if (i_len == 2 && toupper(i[1]) == 'Q') 400 | return ITERATOR_EQ; 401 | break; 402 | case 'G': 403 | if (i_len == 2) { 404 | int second = toupper(i[1]); 405 | switch (second) { 406 | case 'E': 407 | return ITERATOR_GE; 408 | case 'T': 409 | return ITERATOR_GT; 410 | } 411 | } 412 | break; 413 | case 'L': 414 | if (i_len == 2) { 415 | int second = toupper(i[1]); 416 | switch (second) { 417 | case 'T': 418 | return ITERATOR_LT; 419 | case 'E': 420 | return ITERATOR_LE; 421 | } 422 | } 423 | break; 424 | case 'N': 425 | if (i_len == 8 && toupper(i[1]) == 'E' && 426 | toupper(i[2]) == 'I' && toupper(i[3]) == 'G' && 427 | toupper(i[4]) == 'H' && toupper(i[5]) == 'B' && 428 | toupper(i[6]) == 'O' && toupper(i[7]) == 'R') 429 | return ITERATOR_NEIGHBOR; 430 | break; 431 | case 'O': 432 | if (i_len == 8 && toupper(i[1]) == 'V' && 433 | toupper(i[2]) == 'E' && toupper(i[3]) == 'R' && 434 | toupper(i[4]) == 'L' && toupper(i[5]) == 'A' && 435 | toupper(i[6]) == 'P' && toupper(i[7]) == 'S') 436 | return ITERATOR_OVERLAPS; 437 | break; 438 | case 'R': 439 | if (i_len == 3 && toupper(i[1]) == 'E' && toupper(i[2]) == 'Q') 440 | return ITERATOR_REQ; 441 | break; 442 | default: 443 | break; 444 | }; 445 | return -1; 446 | } 447 | 448 | uint32_t php_tp_verify_greetings(char *greetingbuf) { 449 | /* Check basic structure - magic string and \n delimiters */ 450 | if (memcmp(greetingbuf, "Tarantool ", strlen("Tarantool ")) != 0 || 451 | greetingbuf[GREETING_SIZE / 2 - 1] != '\n' || 452 | greetingbuf[GREETING_SIZE - 1] != '\n') 453 | return 0; 454 | 455 | int h = GREETING_SIZE / 2; 456 | const char *pos = greetingbuf + strlen("Tarantool "); 457 | const char *end = greetingbuf + h; 458 | 459 | /* Extract a version string - a string until ' ' */ 460 | char version[20]; 461 | const char *vend = (const char *) memchr(pos, ' ', end - pos); 462 | if (vend == NULL || (size_t)(vend - pos) >= sizeof(version)) 463 | return 0; 464 | memcpy(version, pos, vend - pos); 465 | version[vend - pos] = '\0'; 466 | pos = vend + 1; 467 | for (; pos < end && *pos == ' '; ++pos); /* skip spaces */ 468 | 469 | /* Parse a version string - 1.6.6-83-gc6b2129 or 1.6.7 */ 470 | unsigned major, minor, patch; 471 | if (sscanf(version, "%u.%u.%u", &major, &minor, &patch) != 3) 472 | return 0; 473 | return version_id(major, minor, patch); 474 | } 475 | -------------------------------------------------------------------------------- /src/tarantool_proto.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_PROTO_H 2 | #define PHP_TNT_PROTO_H 3 | 4 | #define SALT64_SIZE 44 5 | #define SALT_SIZE 64 6 | // #define SCRAMBLE_SIZE 20 7 | #define GREETING_SIZE 128 8 | #define SALT_PREFIX_SIZE 64 9 | 10 | #define GREETING_PROTOCOL_LEN_MAX 32 11 | 12 | #define SPACE_SPACE 281 13 | #define SPACE_INDEX 289 14 | 15 | #define INDEX_SPACE_NO 0 16 | #define INDEX_INDEX_NO 0 17 | #define INDEX_SPACE_NAME 2 18 | #define INDEX_INDEX_NAME 2 19 | 20 | #include 21 | 22 | #include 23 | 24 | /** 25 | * Pack version into uint32_t. 26 | * The highest byte or result means major version, next - minor, 27 | * middle - patch, last - revision. 28 | */ 29 | static inline uint32_t 30 | version_id(unsigned major, unsigned minor, unsigned patch) 31 | { 32 | return (((major << 8) | minor) << 8) | patch; 33 | } 34 | 35 | static inline unsigned 36 | version_id_major(uint32_t version_id) 37 | { 38 | return (version_id >> 16) & 0xff; 39 | } 40 | 41 | static inline unsigned 42 | version_id_minor(uint32_t version_id) 43 | { 44 | return (version_id >> 8) & 0xff; 45 | } 46 | 47 | static inline unsigned 48 | version_id_patch(uint32_t version_id) 49 | { 50 | return version_id & 0xff; 51 | } 52 | 53 | 54 | /* header */ 55 | enum tnt_header_key_t { 56 | TNT_CODE = 0x00, 57 | TNT_SYNC = 0x01, 58 | TNT_SCHEMA_ID = 0x05 59 | }; 60 | 61 | /* request body */ 62 | enum tnt_body_key_t { 63 | TNT_SPACE = 0x10, 64 | TNT_INDEX = 0x11, 65 | TNT_LIMIT = 0x12, 66 | TNT_OFFSET = 0x13, 67 | TNT_ITERATOR = 0x14, 68 | TNT_KEY = 0x20, 69 | TNT_TUPLE = 0x21, 70 | TNT_FUNCTION = 0x22, 71 | TNT_USERNAME = 0x23, 72 | TNT_EXPRESSION = 0x27, 73 | TNT_OPS = 0x28 74 | }; 75 | 76 | /* response body */ 77 | enum tnt_response_key_t { 78 | TNT_DATA = 0x30, 79 | TNT_ERROR = 0x31 80 | }; 81 | 82 | /* request types */ 83 | enum tnt_request_type { 84 | TNT_OK = 0x00, 85 | TNT_SELECT = 0x01, 86 | TNT_INSERT = 0x02, 87 | TNT_REPLACE = 0x03, 88 | TNT_UPDATE = 0x04, 89 | TNT_DELETE = 0x05, 90 | TNT_CALL_16 = 0x06, 91 | TNT_AUTH = 0x07, 92 | TNT_EVAL = 0x08, 93 | TNT_UPSERT = 0x09, 94 | TNT_CALL = 0x0a, 95 | TNT_PING = 0x40 96 | }; 97 | 98 | enum tnt_iterator_type { 99 | ITERATOR_EQ = 0, 100 | ITERATOR_REQ = 1, 101 | ITERATOR_ALL = 2, 102 | ITERATOR_LT = 3, 103 | ITERATOR_LE = 4, 104 | ITERATOR_GE = 5, 105 | ITERATOR_GT = 6, 106 | ITERATOR_BITS_ALL_SET = 7, 107 | ITERATOR_BITSET_ALL_SET = 7, 108 | ITERATOR_BITS_ANY_SET = 8, 109 | ITERATOR_BITSET_ANY_SET = 8, 110 | ITERATOR_BITS_ALL_NOT_SET = 9, 111 | ITERATOR_BITSET_ALL_NOT_SET = 9, 112 | ITERATOR_OVERLAPS = 10, 113 | ITERATOR_NEIGHBOR = 11, 114 | }; 115 | 116 | struct tnt_response { 117 | uint64_t bitmap; 118 | const char *buf; 119 | uint32_t code; 120 | uint32_t sync; 121 | const char *error; 122 | size_t error_len; 123 | const char *data; 124 | size_t data_len; 125 | }; 126 | 127 | int64_t php_tp_response(struct tnt_response *r, char *buf, size_t size); 128 | 129 | void php_tp_encode_auth(smart_string *str, uint32_t sync, char * const username, 130 | size_t username_len, char * const scramble); 131 | void php_tp_encode_ping(smart_string *str, uint32_t sync); 132 | void php_tp_encode_select(smart_string *str, uint32_t sync, uint32_t space_no, 133 | uint32_t index_no, uint32_t limit, uint32_t offset, 134 | uint32_t iterator, zval *key); 135 | void php_tp_encode_insert_or_replace(smart_string *str, uint32_t sync, 136 | uint32_t space_no, zval *tuple, 137 | uint32_t type); 138 | void php_tp_encode_delete(smart_string *str, uint32_t sync, uint32_t space_no, 139 | uint32_t index_no, zval *tuple); 140 | void php_tp_encode_call(smart_string *str, uint32_t sync, char *proc, 141 | uint32_t proc_len, zval *tuple); 142 | void php_tp_encode_call_16(smart_string *str, uint32_t sync, char *proc, 143 | uint32_t proc_len, zval *tuple); 144 | void php_tp_encode_eval(smart_string *str, uint32_t sync, char *proc, 145 | uint32_t proc_len, zval *tuple); 146 | 147 | char *php_tp_encode_update(smart_string *str, uint32_t sync, 148 | uint32_t space_no, uint32_t index_no, 149 | zval *key); 150 | char *php_tp_encode_upsert(smart_string *str, uint32_t sync, 151 | uint32_t space_no, zval *tuple); 152 | void php_tp_encode_uheader(smart_string *str, size_t op_count); 153 | void php_tp_encode_uother(smart_string *str, char type, uint32_t fieldno, 154 | zval *value); 155 | void php_tp_encode_usplice(smart_string *str, uint32_t fieldno, 156 | uint32_t position, uint32_t offset, 157 | const char *buffer, size_t buffer_len); 158 | void php_tp_reencode_length(smart_string *str, size_t orig_len); 159 | 160 | int convert_iter_str(const char *i, size_t i_len); 161 | 162 | uint32_t php_tp_verify_greetings(char *greetingbuf); 163 | 164 | #endif /* PHP_TNT_PROTO_H */ 165 | -------------------------------------------------------------------------------- /src/tarantool_schema.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_SCHEMA_H 2 | #define PHP_TNT_SCHEMA_H 3 | 4 | #include 5 | 6 | struct schema_key { 7 | const char *id; 8 | uint32_t id_len; 9 | uint32_t number; 10 | }; 11 | 12 | /** 13 | * Possible field data types. 14 | */ 15 | enum field_type { 16 | FIELD_TYPE_ANY = 0, 17 | FIELD_TYPE_UNSIGNED, 18 | FIELD_TYPE_STRING, 19 | FIELD_TYPE_NUMBER, 20 | FIELD_TYPE_DOUBLE, 21 | FIELD_TYPE_INTEGER, 22 | FIELD_TYPE_BOOLEAN, 23 | FIELD_TYPE_VARBINARY, 24 | FIELD_TYPE_SCALAR, 25 | FIELD_TYPE_DECIMAL, 26 | FIELD_TYPE_UUID, 27 | FIELD_TYPE_ARRAY, 28 | FIELD_TYPE_MAP, 29 | /* Used for unknown type. */ 30 | field_type_MAX 31 | }; 32 | 33 | #define COLL_NONE UINT32_MAX 34 | 35 | struct schema_field_value { 36 | uint32_t field_number; 37 | uint32_t field_name_len; 38 | char *field_name; 39 | enum field_type field_type; 40 | /* Collation ID for string comparison. */ 41 | uint32_t coll_id; 42 | /* True if a key part can store NULLs. */ 43 | bool is_nullable; 44 | }; 45 | 46 | struct schema_index_value { 47 | struct schema_key key; 48 | char *index_name; 49 | uint32_t index_name_len; 50 | uint32_t index_number; 51 | struct schema_field_value *index_parts; 52 | uint32_t index_parts_len; 53 | }; 54 | 55 | struct mh_schema_index_t; 56 | 57 | struct schema_space_value { 58 | struct schema_key key; 59 | char *space_name; 60 | uint32_t space_name_len; 61 | uint32_t space_number; 62 | struct mh_schema_index_t *index_hash; 63 | struct schema_field_value *schema_list; 64 | uint32_t schema_list_len; 65 | }; 66 | 67 | struct mh_schema_space_t; 68 | 69 | struct tarantool_schema { 70 | struct mh_schema_space_t *space_hash; 71 | }; 72 | 73 | int 74 | tarantool_schema_add_spaces(struct tarantool_schema *, const char *, uint32_t); 75 | int 76 | tarantool_schema_add_indexes(struct tarantool_schema *, const char *, uint32_t); 77 | 78 | int32_t 79 | tarantool_schema_get_sid_by_string(struct tarantool_schema *, const char *, 80 | uint32_t); 81 | int32_t 82 | tarantool_schema_get_sid_by_number(struct tarantool_schema *, uint32_t); 83 | int32_t 84 | tarantool_schema_get_iid_by_string(struct tarantool_schema *, uint32_t, 85 | const char *, uint32_t); 86 | int32_t 87 | tarantool_schema_get_fid_by_string(struct tarantool_schema *, uint32_t, 88 | const char *, uint32_t); 89 | 90 | struct tarantool_schema * 91 | tarantool_schema_new(int is_persistent); 92 | void 93 | tarantool_schema_flush(struct tarantool_schema *); 94 | void 95 | tarantool_schema_delete(struct tarantool_schema *, int is_persistent); 96 | 97 | #endif /* PHP_TNT_SCHEMA_H */ 98 | -------------------------------------------------------------------------------- /src/tarantool_tp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "php_tarantool.h" 4 | 5 | #include "tarantool_tp.h" 6 | 7 | struct tp_wrapper { 8 | smart_string *str; 9 | int is_persistent; 10 | }; 11 | 12 | char *tarantool_tp_reserve(struct tp *p, size_t req, size_t *size) { 13 | struct tp_wrapper *wrap = (struct tp_wrapper *)p->obj; 14 | smart_string *str = wrap->str; 15 | if (str->a > str->len + req) 16 | return str->c; 17 | size_t needed = str->a * 2; 18 | if (str->len + req > needed) 19 | needed = str->len + req; 20 | register size_t __n1; 21 | smart_string_alloc4(str, needed, wrap->is_persistent, __n1); 22 | return str->c; 23 | } 24 | 25 | struct tp *tarantool_tp_new(smart_string *s, int is_persistent) { 26 | struct tp *tps = pecalloc(1, sizeof(struct tp), is_persistent); 27 | struct tp_wrapper *wrap = pecalloc(1, sizeof(struct tp_wrapper), 28 | is_persistent); 29 | wrap->str = s; 30 | wrap->is_persistent = is_persistent; 31 | tp_init(tps, s->c, s->a, tarantool_tp_reserve, wrap); 32 | return tps; 33 | } 34 | 35 | void tarantool_tp_free(struct tp* tps, int is_persistent) { 36 | pefree(tps->obj, is_persistent); 37 | pefree(tps, is_persistent); 38 | } 39 | 40 | void tarantool_tp_flush(struct tp* tps) { 41 | tps->p = tps->s; 42 | tps->size = NULL; 43 | tps->sync = NULL; 44 | } 45 | 46 | void tarantool_tp_update(struct tp* tps) { 47 | tps->s = ((struct tp_wrapper *)(tps->obj))->str->c; 48 | tps->p = tps->s; 49 | tps->e = tps->s + ((struct tp_wrapper *)(tps->obj))->str->a; 50 | } 51 | -------------------------------------------------------------------------------- /src/tarantool_tp.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_TP_H 2 | #define PHP_TNT_TP_H 3 | 4 | #include "src/third_party/tp.h" 5 | 6 | struct tp * 7 | tarantool_tp_new(smart_string *s, int is_persistent); 8 | 9 | void tarantool_tp_free(struct tp* tps, int is_persistent); 10 | void tarantool_tp_flush(struct tp* tps); 11 | void tarantool_tp_update(struct tp* tps); 12 | 13 | #endif /* PHP_TNT_TP_H */ 14 | -------------------------------------------------------------------------------- /src/third_party/PMurHash.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | * MurmurHash3 was written by Austin Appleby, and is placed in the public 3 | * domain. 4 | * 5 | * This implementation was written by Shane Day, and is also public domain. 6 | * 7 | * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) 8 | * with support for progressive processing. 9 | */ 10 | 11 | /*----------------------------------------------------------------------------- 12 | 13 | If you want to understand the MurmurHash algorithm you would be much better 14 | off reading the original source. Just point your browser at: 15 | http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp 16 | 17 | 18 | What this version provides? 19 | 20 | 1. Progressive data feeding. Useful when the entire payload to be hashed 21 | does not fit in memory or when the data is streamed through the application. 22 | Also useful when hashing a number of strings with a common prefix. A partial 23 | hash of a prefix string can be generated and reused for each suffix string. 24 | 25 | 2. Portability. Plain old C so that it should compile on any old compiler. 26 | Both CPU endian and access-alignment neutral, but avoiding inefficient code 27 | when possible depending on CPU capabilities. 28 | 29 | 3. Drop in. I personally like nice self contained public domain code, making it 30 | easy to pilfer without loads of refactoring to work properly in the existing 31 | application code & makefile structure and mucking around with licence files. 32 | Just copy PMurHash.h and PMurHash.c and you're ready to go. 33 | 34 | 35 | How does it work? 36 | 37 | We can only process entire 32 bit chunks of input, except for the very end 38 | that may be shorter. So along with the partial hash we need to give back to 39 | the caller a carry containing up to 3 bytes that we were unable to process. 40 | This carry also needs to record the number of bytes the carry holds. I use 41 | the low 2 bits as a count (0..3) and the carry bytes are shifted into the 42 | high byte in stream order. 43 | 44 | To handle endianess I simply use a macro that reads a uint32_t and define 45 | that macro to be a direct read on little endian machines, a read and swap 46 | on big endian machines, or a byte-by-byte read if the endianess is unknown. 47 | 48 | -----------------------------------------------------------------------------*/ 49 | 50 | 51 | #include "PMurHash.h" 52 | 53 | /* I used ugly type names in the header to avoid potential conflicts with 54 | * application or system typedefs & defines. Since I'm not including any more 55 | * headers below here I can rename these so that the code reads like C99 */ 56 | #undef uint32_t 57 | #define uint32_t MH_UINT32 58 | #undef uint8_t 59 | #define uint8_t MH_UINT8 60 | 61 | /* MSVC warnings we choose to ignore */ 62 | #if defined(_MSC_VER) 63 | #pragma warning(disable: 4127) /* conditional expression is constant */ 64 | #endif 65 | 66 | /*----------------------------------------------------------------------------- 67 | * Endianess, misalignment capabilities and util macros 68 | * 69 | * The following 3 macros are defined in this section. The other macros defined 70 | * are only needed to help derive these 3. 71 | * 72 | * READ_UINT32(x) Read a little endian unsigned 32-bit int 73 | * UNALIGNED_SAFE Defined if READ_UINT32 works on non-word boundaries 74 | * ROTL32(x,r) Rotate x left by r bits 75 | */ 76 | 77 | /* Convention is to define __BYTE_ORDER == to one of these values */ 78 | #if !defined(__BIG_ENDIAN) 79 | #define __BIG_ENDIAN 4321 80 | #endif 81 | #if !defined(__LITTLE_ENDIAN) 82 | #define __LITTLE_ENDIAN 1234 83 | #endif 84 | 85 | /* I386 */ 86 | #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386) 87 | #define __BYTE_ORDER __LITTLE_ENDIAN 88 | #define UNALIGNED_SAFE 89 | #endif 90 | 91 | /* gcc 'may' define __LITTLE_ENDIAN__ or __BIG_ENDIAN__ to 1 (Note the trailing __), 92 | * or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */ 93 | #if !defined(__BYTE_ORDER) 94 | #if defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1 95 | #define __BYTE_ORDER __LITTLE_ENDIAN 96 | #elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1 97 | #define __BYTE_ORDER __BIG_ENDIAN 98 | #endif 99 | #endif 100 | 101 | /* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */ 102 | #if !defined(__BYTE_ORDER) 103 | #if defined(__ARMEL__) || defined(__MIPSEL__) 104 | #define __BYTE_ORDER __LITTLE_ENDIAN 105 | #endif 106 | #if defined(__ARMEB__) || defined(__MIPSEB__) 107 | #define __BYTE_ORDER __BIG_ENDIAN 108 | #endif 109 | #endif 110 | 111 | /* Now find best way we can to READ_UINT32 */ 112 | #if __BYTE_ORDER==__LITTLE_ENDIAN 113 | /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ 114 | #define READ_UINT32(ptr) (*((uint32_t*)(ptr))) 115 | #elif __BYTE_ORDER==__BIG_ENDIAN 116 | /* TODO: Add additional cases below where a compiler provided bswap32 is available */ 117 | #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) 118 | #define READ_UINT32(ptr) (__builtin_bswap32(*((uint32_t*)(ptr)))) 119 | #else 120 | /* Without a known fast bswap32 we're just as well off doing this */ 121 | #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) 122 | #define UNALIGNED_SAFE 123 | #endif 124 | #else 125 | /* Unknown endianess so last resort is to read individual bytes */ 126 | #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) 127 | 128 | /* Since we're not doing word-reads we can skip the messing about with realignment */ 129 | #define UNALIGNED_SAFE 130 | #endif 131 | 132 | /* Find best way to ROTL32 */ 133 | #if defined(_MSC_VER) 134 | #include /* Microsoft put _rotl declaration in here */ 135 | #define ROTL32(x,r) _rotl(x,r) 136 | #else 137 | /* gcc recognises this code and generates a rotate instruction for CPUs with one */ 138 | #define ROTL32(x,r) (((uint32_t)x << r) | ((uint32_t)x >> (32 - r))) 139 | #endif 140 | 141 | 142 | /*----------------------------------------------------------------------------- 143 | * Core murmurhash algorithm macros */ 144 | 145 | #define C1 (0xcc9e2d51) 146 | #define C2 (0x1b873593) 147 | 148 | /* This is the main processing body of the algorithm. It operates 149 | * on each full 32-bits of input. */ 150 | #define DOBLOCK(h1, k1) do{ \ 151 | k1 *= C1; \ 152 | k1 = ROTL32(k1,15); \ 153 | k1 *= C2; \ 154 | \ 155 | h1 ^= k1; \ 156 | h1 = ROTL32(h1,13); \ 157 | h1 = h1*5+0xe6546b64; \ 158 | }while(0) 159 | 160 | 161 | /* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ 162 | /* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ 163 | #define DOBYTES(cnt, h1, c, n, ptr, len) do{ \ 164 | int _i = cnt; \ 165 | while(_i--) { \ 166 | c = c>>8 | *ptr++<<24; \ 167 | n++; len--; \ 168 | if(n==4) { \ 169 | DOBLOCK(h1, c); \ 170 | n = 0; \ 171 | } \ 172 | } }while(0) 173 | 174 | /*---------------------------------------------------------------------------*/ 175 | 176 | /* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed 177 | * if wanted. Both ph1 and pcarry are required arguments. */ 178 | void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int len) 179 | { 180 | uint32_t h1 = *ph1; 181 | uint32_t c = *pcarry; 182 | 183 | const uint8_t *ptr = (uint8_t*)key; 184 | const uint8_t *end; 185 | 186 | /* Extract carry count from low 2 bits of c value */ 187 | int n = c & 3; 188 | 189 | #if defined(UNALIGNED_SAFE) 190 | /* This CPU handles unaligned word access */ 191 | 192 | /* Consume any carry bytes */ 193 | int i = (4-n) & 3; 194 | if(i && i <= len) { 195 | DOBYTES(i, h1, c, n, ptr, len); 196 | } 197 | 198 | /* Process 32-bit chunks */ 199 | end = ptr + len/4*4; 200 | for( ; ptr < end ; ptr+=4) { 201 | uint32_t k1 = READ_UINT32(ptr); 202 | DOBLOCK(h1, k1); 203 | } 204 | 205 | #else /*UNALIGNED_SAFE*/ 206 | /* This CPU does not handle unaligned word access */ 207 | 208 | /* Consume enough so that the next data byte is word aligned */ 209 | int i = -(long)ptr & 3; 210 | if(i && i <= len) { 211 | DOBYTES(i, h1, c, n, ptr, len); 212 | } 213 | 214 | /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ 215 | end = ptr + len/4*4; 216 | switch(n) { /* how many bytes in c */ 217 | case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ 218 | for( ; ptr < end ; ptr+=4) { 219 | uint32_t k1 = READ_UINT32(ptr); 220 | DOBLOCK(h1, k1); 221 | } 222 | break; 223 | case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ 224 | for( ; ptr < end ; ptr+=4) { 225 | uint32_t k1 = c>>24; 226 | c = READ_UINT32(ptr); 227 | k1 |= c<<8; 228 | DOBLOCK(h1, k1); 229 | } 230 | break; 231 | case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ 232 | for( ; ptr < end ; ptr+=4) { 233 | uint32_t k1 = c>>16; 234 | c = READ_UINT32(ptr); 235 | k1 |= c<<16; 236 | DOBLOCK(h1, k1); 237 | } 238 | break; 239 | case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ 240 | for( ; ptr < end ; ptr+=4) { 241 | uint32_t k1 = c>>8; 242 | c = READ_UINT32(ptr); 243 | k1 |= c<<24; 244 | DOBLOCK(h1, k1); 245 | } 246 | } 247 | #endif /*UNALIGNED_SAFE*/ 248 | 249 | /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ 250 | len -= len/4*4; 251 | 252 | /* Append any remaining bytes into carry */ 253 | DOBYTES(len, h1, c, n, ptr, len); 254 | 255 | /* Copy out new running hash and carry */ 256 | *ph1 = h1; 257 | *pcarry = (c & ~0xff) | n; 258 | } 259 | 260 | /*---------------------------------------------------------------------------*/ 261 | 262 | /* Finalize a hash. To match the original Murmur3A the total_length must be provided */ 263 | uint32_t PMurHash32_Result(uint32_t h, uint32_t carry, uint32_t total_length) 264 | { 265 | uint32_t k1; 266 | int n = carry & 3; 267 | if(n) { 268 | k1 = carry >> (4-n)*8; 269 | k1 *= C1; k1 = ROTL32(k1,15); k1 *= C2; h ^= k1; 270 | } 271 | h ^= total_length; 272 | 273 | /* fmix */ 274 | h ^= h >> 16; 275 | h *= 0x85ebca6b; 276 | h ^= h >> 13; 277 | h *= 0xc2b2ae35; 278 | h ^= h >> 16; 279 | 280 | return h; 281 | } 282 | 283 | /*---------------------------------------------------------------------------*/ 284 | 285 | /* Murmur3A compatable all-at-once */ 286 | uint32_t PMurHash32(uint32_t seed, const void *key, int len) 287 | { 288 | uint32_t h1=seed, carry=0; 289 | PMurHash32_Process(&h1, &carry, key, len); 290 | return PMurHash32_Result(h1, carry, len); 291 | } 292 | 293 | /*---------------------------------------------------------------------------*/ 294 | 295 | /* Provide an API suitable for smhasher */ 296 | void PMurHash32_test(const void *key, int len, uint32_t seed, void *out) 297 | { 298 | uint32_t h1=seed, carry=0; 299 | const uint8_t *ptr = (uint8_t*)key; 300 | const uint8_t *end = ptr + len; 301 | 302 | #if 0 /* Exercise the progressive processing */ 303 | while(ptr < end) { 304 | //const uint8_t *mid = ptr + rand()%(end-ptr)+1; 305 | const uint8_t *mid = ptr + (rand()&0xF); 306 | mid = mid= 199901L ) 27 | #include 28 | #define MH_UINT32 uint32_t 29 | #endif 30 | 31 | /* Otherwise try testing against max value macros from limit.h */ 32 | #if !defined(MH_UINT32) 33 | #include 34 | #if (USHRT_MAX == 0xffffffffUL) 35 | #define MH_UINT32 unsigned short 36 | #elif (UINT_MAX == 0xffffffffUL) 37 | #define MH_UINT32 unsigned int 38 | #elif (ULONG_MAX == 0xffffffffUL) 39 | #define MH_UINT32 unsigned long 40 | #endif 41 | #endif 42 | 43 | #if !defined(MH_UINT32) 44 | #error Unable to determine type name for unsigned 32-bit int 45 | #endif 46 | 47 | /* I'm yet to work on a platform where 'unsigned char' is not 8 bits */ 48 | #define MH_UINT8 unsigned char 49 | 50 | 51 | /* ------------------------------------------------------------------------- */ 52 | /* Prototypes */ 53 | 54 | #ifdef __cplusplus 55 | extern "C" { 56 | #endif 57 | 58 | void PMurHash32_Process(MH_UINT32 *ph1, MH_UINT32 *pcarry, const void *key, int len); 59 | MH_UINT32 PMurHash32_Result(MH_UINT32 h1, MH_UINT32 carry, MH_UINT32 total_length); 60 | MH_UINT32 PMurHash32(MH_UINT32 seed, const void *key, int len); 61 | 62 | void PMurHash32_test(const void *key, int len, MH_UINT32 seed, void *out); 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/third_party/mhash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Redistribution and use in source and binary forms, with or 3 | * without modification, are permitted provided that the following 4 | * conditions are met: 5 | * 6 | * 1. Redistributions of source code must retain the above 7 | * copyright notice, this list of conditions and the 8 | * following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above 11 | * copyright notice, this list of conditions and the following 12 | * disclaimer in the documentation and/or other materials 13 | * provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 26 | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | /* The MIT License 30 | 31 | Copyright (c) 2008, by Attractive Chaos 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining 34 | a copy of this software and associated documentation files (the 35 | "Software"), to deal in the Software without restriction, including 36 | without limitation the rights to use, copy, modify, merge, publish, 37 | distribute, sublicense, and/or sell copies of the Software, and to 38 | permit persons to whom the Software is furnished to do so, subject to 39 | the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be 42 | included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 47 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 48 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 49 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 50 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | */ 53 | 54 | #ifndef MH_INCREMENTAL_RESIZE 55 | #define MH_INCREMENTAL_RESIZE 1 56 | #endif 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | #define mh_cat(a, b) mh##a##_##b 64 | #define mh_ecat(a, b) mh_cat(a, b) 65 | #define _mh(x) mh_ecat(mh_name, x) 66 | 67 | #define mh_unlikely(x) __builtin_expect((x),0) 68 | 69 | #ifndef MH_CALLOC 70 | #define MH_CALLOC calloc 71 | #endif /* MH_CALLOC */ 72 | 73 | #ifndef MH_FREE 74 | #define MH_FREE free 75 | #endif /* MH_FREE */ 76 | 77 | #ifndef MH_TYPEDEFS 78 | #define MH_TYPEDEFS 1 79 | typedef uint32_t mh_int_t; 80 | #endif /* MH_TYPEDEFS */ 81 | 82 | #ifndef __ac_HASH_PRIME_SIZE 83 | #define __ac_HASH_PRIME_SIZE 31 84 | static const mh_int_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = { 85 | 3ul, 11ul, 23ul, 53ul, 86 | 97ul, 193ul, 389ul, 769ul, 87 | 1543ul, 3079ul, 6151ul, 12289ul, 88 | 24593ul, 49157ul, 98317ul, 196613ul, 89 | 393241ul, 786433ul, 1572869ul, 3145739ul, 90 | 6291469ul, 12582917ul, 25165843ul, 50331653ul, 91 | 100663319ul, 201326611ul, 402653189ul, 805306457ul, 92 | 1610612741ul, 3221225473ul, 4294967291ul 93 | }; 94 | #endif /* __ac_HASH_PRIME_SIZE */ 95 | 96 | #ifndef MH_HEADER 97 | #define MH_HEADER 98 | 99 | struct _mh(t) { 100 | mh_node_t *p; 101 | mh_int_t *b; 102 | mh_int_t n_buckets; 103 | mh_int_t n_dirty; 104 | mh_int_t size; 105 | mh_int_t upper_bound; 106 | mh_int_t prime; 107 | 108 | mh_int_t resize_cnt; 109 | mh_int_t resize_position; 110 | mh_int_t batch; 111 | struct _mh(t) *shadow; 112 | }; 113 | 114 | #define mh_exist(h, i) ({ h->b[i >> 4] & (1 << (i % 16)); }) 115 | #define mh_dirty(h, i) ({ h->b[i >> 4] & (1 << (i % 16 + 16)); }) 116 | 117 | #define mh_setfree(h, i) ({ h->b[i >> 4] &= ~(1 << (i % 16)); }) 118 | #define mh_setexist(h, i) ({ h->b[i >> 4] |= (1 << (i % 16)); }) 119 | #define mh_setdirty(h, i) ({ h->b[i >> 4] |= (1 << (i % 16 + 16)); }) 120 | 121 | #define mh_node(h, i) ((const mh_node_t *) &((h)->p[(i)])) 122 | #define mh_size(h) ({ (h)->size; }) 123 | #define mh_capacity(h) ({ (h)->n_buckets; }) 124 | #define mh_begin(h) ({ 0; }) 125 | #define mh_end(h) ({ (h)->n_buckets; }) 126 | 127 | #define mh_first(h) ({ \ 128 | mh_int_t i; \ 129 | for (i = 0; i < mh_end(h); i++) { \ 130 | if (mh_exist(h, i)) \ 131 | break; \ 132 | } \ 133 | i; \ 134 | }) 135 | 136 | #define mh_next(h, i) ({ \ 137 | mh_int_t n = i; \ 138 | if (n < mh_end(h)) { \ 139 | for (n = i + 1; n < mh_end(h); n++) { \ 140 | if (mh_exist(h, n)) \ 141 | break; \ 142 | } \ 143 | } \ 144 | n; \ 145 | }) 146 | 147 | #define mh_foreach(h, i) \ 148 | for (i = mh_first(h); i < mh_end(h); i = mh_next(h, i)) 149 | 150 | #define MH_DENSITY 0.7 151 | 152 | struct _mh(t) * _mh(new)(); 153 | void _mh(clear)(struct _mh(t) *h); 154 | void _mh(delete)(struct _mh(t) *h); 155 | void _mh(resize)(struct _mh(t) *h, mh_arg_t arg); 156 | int _mh(start_resize)(struct _mh(t) *h, mh_int_t buckets, mh_int_t batch, 157 | mh_arg_t arg); 158 | void _mh(reserve)(struct _mh(t) *h, mh_int_t size, 159 | mh_arg_t arg); 160 | void __attribute__((noinline)) _mh(del_resize)(struct _mh(t) *h, mh_int_t x, 161 | mh_arg_t arg); 162 | size_t _mh(memsize)(struct _mh(t) *h); 163 | void _mh(dump)(struct _mh(t) *h); 164 | 165 | #define put_slot(h, node, arg) \ 166 | _mh(put_slot)(h, node, arg) 167 | 168 | static inline mh_node_t * 169 | _mh(node)(struct _mh(t) *h, mh_int_t x) 170 | { 171 | return (mh_node_t *) &(h->p[x]); 172 | } 173 | 174 | 175 | static inline mh_int_t 176 | _mh(next_slot)(mh_int_t slot, mh_int_t inc, mh_int_t size) 177 | { 178 | slot += inc; 179 | return slot >= size ? slot - size : slot; 180 | } 181 | 182 | #if defined(mh_hash_key) && defined(mh_eq_key) 183 | /** 184 | * If it is necessary to search by something different 185 | * than a hash node, define mh_hash_key and mh_eq_key 186 | * and use mh_find(). 187 | */ 188 | static inline mh_int_t 189 | _mh(find)(struct _mh(t) *h, mh_key_t key, mh_arg_t arg) 190 | { 191 | (void) arg; 192 | 193 | mh_int_t k = mh_hash_key(key, arg); 194 | mh_int_t i = k % h->n_buckets; 195 | mh_int_t inc = 1 + k % (h->n_buckets - 1); 196 | for (;;) { 197 | if ((mh_exist(h, i) && mh_eq_key(key, mh_node(h, i), arg))) 198 | return i; 199 | 200 | if (!mh_dirty(h, i)) 201 | return h->n_buckets; 202 | 203 | i = _mh(next_slot)(i, inc, h->n_buckets); 204 | } 205 | } 206 | #endif 207 | 208 | static inline mh_int_t 209 | _mh(get)(struct _mh(t) *h, const mh_node_t *node, 210 | mh_arg_t arg) 211 | { 212 | (void) arg; 213 | 214 | mh_int_t k = mh_hash(node, arg); 215 | mh_int_t i = k % h->n_buckets; 216 | mh_int_t inc = 1 + k % (h->n_buckets - 1); 217 | for (;;) { 218 | if ((mh_exist(h, i) && mh_eq(node, mh_node(h, i), arg))) 219 | return i; 220 | 221 | if (!mh_dirty(h, i)) 222 | return h->n_buckets; 223 | 224 | i = _mh(next_slot)(i, inc, h->n_buckets); 225 | } 226 | } 227 | 228 | static inline mh_int_t 229 | _mh(random)(struct _mh(t) *h, mh_int_t rnd) 230 | { 231 | mh_int_t i = 0; 232 | for (i = 0; i < mh_size(h); i++, rnd++) { 233 | rnd %= h->n_buckets; 234 | if (mh_exist(h, rnd)) 235 | return rnd; 236 | } 237 | 238 | return h->n_buckets; 239 | } 240 | 241 | static inline mh_int_t 242 | _mh(put_slot)(struct _mh(t) *h, const mh_node_t *node, 243 | mh_arg_t arg) 244 | { 245 | (void) arg; 246 | 247 | mh_int_t k = mh_hash(node, arg); /* hash key */ 248 | mh_int_t i = k % h->n_buckets; /* offset in the hash table. */ 249 | mh_int_t inc = 1 + k % (h->n_buckets - 1); /* overflow chain increment. */ 250 | 251 | /* Skip through all collisions. */ 252 | while (mh_exist(h, i)) { 253 | if (mh_eq(node, mh_node(h, i), arg)) 254 | return i; /* Found a duplicate. */ 255 | /* 256 | * Mark this link as part of a collision chain. The 257 | * chain always ends with a non-marked link. 258 | * Note: the collision chain for this key may share 259 | * links with collision chains of other keys. 260 | */ 261 | mh_setdirty(h, i); 262 | i = _mh(next_slot)(i, inc, h->n_buckets); 263 | } 264 | /* 265 | * Found an unused, but possibly dirty slot. Use it. 266 | * However, if this is a dirty slot, first check that 267 | * there are no duplicates down the collision chain. The 268 | * current link can also be from a collision chain of some 269 | * other key, but this is can't be established, so check 270 | * anyway. 271 | */ 272 | mh_int_t save_i = i; 273 | while (mh_dirty(h, i)) { 274 | i = _mh(next_slot)(i, inc, h->n_buckets); 275 | 276 | if (mh_exist(h, i) && mh_eq(mh_node(h, i), node, arg)) 277 | return i; /* Found a duplicate. */ 278 | } 279 | /* Reached the end of the collision chain: no duplicates. */ 280 | return save_i; 281 | } 282 | 283 | /** 284 | * Find a node in the hash and replace it with a new value. 285 | * Save the old node in ret pointer, if it is provided. 286 | * If the old node didn't exist, just insert the new node. 287 | * 288 | * @retval != mh_end() pos of the new node, ret is either NULL 289 | * or copy of the old node 290 | * @retval mh_end() out of memory, ret is unchanged. 291 | */ 292 | static inline mh_int_t 293 | _mh(put)(struct _mh(t) *h, const mh_node_t *node, mh_node_t **ret, 294 | mh_arg_t arg) 295 | { 296 | mh_int_t x = mh_end(h); 297 | int exist; 298 | if (h->size == h->n_buckets) 299 | /* no one free elements in the hash table */ 300 | goto put_done; 301 | 302 | #if MH_INCREMENTAL_RESIZE 303 | if (mh_unlikely(h->resize_position > 0)) 304 | _mh(resize)(h, arg); 305 | else if (mh_unlikely(h->n_dirty >= h->upper_bound)) { 306 | if (_mh(start_resize)(h, h->n_buckets + 1, 0, arg)) 307 | goto put_done; 308 | } 309 | if (h->resize_position) 310 | _mh(put)(h->shadow, node, NULL, arg); 311 | #else 312 | if (mh_unlikely(h->n_dirty >= h->upper_bound)) { 313 | if (_mh(start_resize)(h, h->n_buckets + 1, h->size, 314 | arg)) 315 | goto put_done; 316 | } 317 | #endif 318 | 319 | x = put_slot(h, node, arg); 320 | exist = mh_exist(h, x); 321 | 322 | if (!exist) { 323 | /* add new */ 324 | mh_setexist(h, x); 325 | h->size++; 326 | if (!mh_dirty(h, x)) 327 | h->n_dirty++; 328 | 329 | memcpy(&(h->p[x]), node, sizeof(mh_node_t)); 330 | if (ret) 331 | *ret = NULL; 332 | } else { 333 | if (ret) 334 | memcpy(*ret, &(h->p[x]), sizeof(mh_node_t)); 335 | /* replace old */ 336 | memcpy(&(h->p[x]), node, sizeof(mh_node_t)); 337 | } 338 | 339 | put_done: 340 | return x; 341 | } 342 | 343 | static inline void 344 | _mh(del)(struct _mh(t) *h, mh_int_t x, 345 | mh_arg_t arg) 346 | { 347 | if (x != h->n_buckets && mh_exist(h, x)) { 348 | mh_setfree(h, x); 349 | h->size--; 350 | if (!mh_dirty(h, x)) 351 | h->n_dirty--; 352 | #if MH_INCREMENTAL_RESIZE 353 | if (mh_unlikely(h->resize_position)) 354 | _mh(del_resize)(h, x, arg); 355 | #endif 356 | } 357 | } 358 | #endif 359 | 360 | static inline void 361 | _mh(remove)(struct _mh(t) *h, const mh_node_t *node, 362 | mh_arg_t arg) 363 | { 364 | mh_int_t k = _mh(get)(h, node, arg); 365 | if (k != mh_end(h)) 366 | _mh(del)(h, k, arg); 367 | } 368 | 369 | #ifdef MH_SOURCE 370 | 371 | void __attribute__((noinline)) 372 | _mh(del_resize)(struct _mh(t) *h, mh_int_t x, 373 | mh_arg_t arg) 374 | { 375 | struct _mh(t) *s = h->shadow; 376 | uint32_t y = _mh(get)(s, (const mh_node_t *) &(h->p[x]), 377 | arg); 378 | _mh(del)(s, y, arg); 379 | _mh(resize)(h, arg); 380 | } 381 | 382 | struct _mh(t) * 383 | _mh(new)() 384 | { 385 | struct _mh(t) *h = (struct _mh(t) *) MH_CALLOC(1, sizeof(*h)); 386 | h->shadow = (struct _mh(t) *) MH_CALLOC(1, sizeof(*h)); 387 | h->prime = 0; 388 | h->n_buckets = __ac_prime_list[h->prime]; 389 | h->p = (mh_node_t *) MH_CALLOC(h->n_buckets, sizeof(mh_node_t)); 390 | h->b = (mh_int_t *) MH_CALLOC(h->n_buckets / 16 + 1, sizeof(mh_int_t)); 391 | h->upper_bound = h->n_buckets * MH_DENSITY; 392 | return h; 393 | } 394 | 395 | void 396 | _mh(clear)(struct _mh(t) *h) 397 | { 398 | MH_FREE(h->p); 399 | h->prime = 0; 400 | h->n_buckets = __ac_prime_list[h->prime]; 401 | h->p = (mh_node_t *) MH_CALLOC(h->n_buckets, sizeof(mh_node_t)); 402 | h->upper_bound = h->n_buckets * MH_DENSITY; 403 | } 404 | 405 | void 406 | _mh(delete)(struct _mh(t) *h) 407 | { 408 | MH_FREE(h->shadow); 409 | MH_FREE(h->b); 410 | MH_FREE(h->p); 411 | MH_FREE(h); 412 | } 413 | 414 | /** Calculate hash size. */ 415 | size_t 416 | _mh(memsize)(struct _mh(t) *h) 417 | { 418 | size_t sz = 2 * sizeof(struct _mh(t)); 419 | 420 | sz += h->n_buckets * sizeof(mh_node_t); 421 | sz += (h->n_buckets / 16 + 1) * sizeof(mh_int_t); 422 | if (h->resize_position) { 423 | h = h->shadow; 424 | sz += h->n_buckets * sizeof(mh_node_t); 425 | sz += (h->n_buckets / 16 + 1) * sizeof(mh_int_t); 426 | } 427 | return sz; 428 | } 429 | 430 | void 431 | _mh(resize)(struct _mh(t) *h, 432 | mh_arg_t arg) 433 | { 434 | struct _mh(t) *s = h->shadow; 435 | #if MH_INCREMENTAL_RESIZE 436 | mh_int_t batch = h->batch; 437 | #endif 438 | mh_int_t i = 0; 439 | for (i = h->resize_position; i < h->n_buckets; i++) { 440 | #if MH_INCREMENTAL_RESIZE 441 | if (batch-- == 0) { 442 | h->resize_position = i; 443 | return; 444 | } 445 | #endif 446 | if (!mh_exist(h, i)) 447 | continue; 448 | mh_int_t n = put_slot(s, mh_node(h, i), arg); 449 | s->p[n] = h->p[i]; 450 | mh_setexist(s, n); 451 | s->n_dirty++; 452 | } 453 | MH_FREE(h->p); 454 | MH_FREE(h->b); 455 | s->size = h->size; 456 | memcpy(h, s, sizeof(*h)); 457 | h->resize_cnt++; 458 | } 459 | 460 | int 461 | _mh(start_resize)(struct _mh(t) *h, mh_int_t buckets, mh_int_t batch, 462 | mh_arg_t arg) 463 | { 464 | if (h->resize_position) { 465 | /* resize has already been started */ 466 | return 0; 467 | } 468 | if (buckets < h->n_buckets) { 469 | /* hash size is already greater than requested */ 470 | return 0; 471 | } 472 | while (h->prime < __ac_HASH_PRIME_SIZE) { 473 | if (__ac_prime_list[h->prime] >= buckets) 474 | break; 475 | h->prime += 1; 476 | } 477 | 478 | h->batch = batch > 0 ? batch : h->n_buckets / (256 * 1024); 479 | if (h->batch < 256) { 480 | /* 481 | * Minimal batch must be greater or equal to 482 | * 1 / (1 - f), where f is upper bound percent 483 | * = MH_DENSITY 484 | */ 485 | h->batch = 256; 486 | } 487 | 488 | struct _mh(t) *s = h->shadow; 489 | memcpy(s, h, sizeof(*h)); 490 | s->resize_position = 0; 491 | s->n_buckets = __ac_prime_list[h->prime]; 492 | s->upper_bound = s->n_buckets * MH_DENSITY; 493 | s->n_dirty = 0; 494 | s->p = (mh_node_t *) MH_CALLOC(s->n_buckets, sizeof(mh_node_t)); 495 | if (s->p == NULL) 496 | return -1; 497 | s->b = (mh_int_t *) MH_CALLOC(s->n_buckets / 16 + 1, sizeof(mh_int_t)); 498 | if (s->b == NULL) { 499 | MH_FREE(s->p); 500 | s->p = NULL; 501 | return -1; 502 | } 503 | _mh(resize)(h, arg); 504 | 505 | return 0; 506 | } 507 | 508 | void 509 | _mh(reserve)(struct _mh(t) *h, mh_int_t size, 510 | mh_arg_t arg) 511 | { 512 | _mh(start_resize)(h, size/MH_DENSITY, h->size, arg); 513 | } 514 | 515 | #ifndef mh_stat 516 | #define mh_stat(buf, h) ({ \ 517 | tbuf_printf(buf, " n_buckets: %" PRIu32 CRLF \ 518 | " n_dirty: %" PRIu32 CRLF \ 519 | " size: %" PRIu32 CRLF \ 520 | " resize_cnt: %" PRIu32 CRLF \ 521 | " resize_position: %" PRIu32 CRLF, \ 522 | h->n_buckets, \ 523 | h->n_dirty, \ 524 | h->size, \ 525 | h->resize_cnt, \ 526 | h->resize_position); \ 527 | }) 528 | #endif 529 | 530 | #ifdef MH_DEBUG 531 | void 532 | _mh(dump)(struct _mh(t) *h) 533 | { 534 | printf("slots:\n"); 535 | int k = 0, i = 0; 536 | for(i = 0; i < h->n_buckets; i++) { 537 | if (mh_dirty(h, i) || mh_exist(h, i)) { 538 | printf(" [%i] ", i); 539 | if (mh_exist(h, i)) { 540 | /* TODO(roman): fix this printf */ 541 | printf(" -> %p", h->p[i]); 542 | k++; 543 | } 544 | if (mh_dirty(h, i)) 545 | printf(" dirty"); 546 | printf("\n"); 547 | } 548 | } 549 | printf("end(%i)\n", k); 550 | } 551 | #endif 552 | 553 | #endif 554 | 555 | #if defined(MH_SOURCE) || defined(MH_UNDEF) 556 | #undef MH_CALLOC 557 | #undef MH_FREE 558 | #undef MH_HEADER 559 | #undef mh_node_t 560 | #undef mh_arg_t 561 | #undef mh_key_t 562 | #undef mh_name 563 | #undef mh_hash 564 | #undef mh_hash_key 565 | #undef mh_eq 566 | #undef mh_eq_key 567 | #undef mh_node 568 | #undef mh_dirty 569 | #undef mh_place 570 | #undef mh_setdirty 571 | #undef mh_setexist 572 | #undef mh_setvalue 573 | #undef mh_unlikely 574 | #undef slot 575 | #undef slot_and_dirty 576 | #undef MH_DENSITY 577 | #endif 578 | 579 | #undef mh_cat 580 | #undef mh_ecat 581 | #undef _mh 582 | -------------------------------------------------------------------------------- /src/third_party/msgpuck.c: -------------------------------------------------------------------------------- 1 | #define MP_SOURCE 1 2 | 3 | #include "msgpuck.h" 4 | -------------------------------------------------------------------------------- /src/third_party/sha1.c: -------------------------------------------------------------------------------- 1 | 2 | /* from valgrind tests */ 3 | 4 | /* ================ sha1.c ================ */ 5 | /* 6 | SHA-1 in C 7 | By Steve Reid 8 | 100% Public Domain 9 | 10 | Test Vectors (from FIPS PUB 180-1) 11 | "abc" 12 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 13 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 14 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 15 | A million repetitions of "a" 16 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 17 | */ 18 | 19 | /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ 20 | /* #define SHA1HANDSOFF * Copies data before messing with it. */ 21 | 22 | #define SHA1HANDSOFF 23 | 24 | #include 25 | #include 26 | #include /* for u_int*_t */ 27 | #include "sha1.h" 28 | 29 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 30 | 31 | /* blk0() and blk() perform the initial expand. */ 32 | /* I got the idea of expanding during the round function from SSLeay */ 33 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 34 | |(rol(block->l[i],8)&0x00FF00FF)) 35 | 36 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 37 | ^block->l[(i+2)&15]^block->l[i&15],1)) 38 | 39 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 40 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 41 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 42 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 43 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 44 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 45 | 46 | 47 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 48 | 49 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) 50 | { 51 | uint32_t a, b, c, d, e; 52 | typedef union { 53 | unsigned char c[64]; 54 | uint32_t l[16]; 55 | } CHAR64LONG16; 56 | #ifdef SHA1HANDSOFF 57 | CHAR64LONG16 block[1]; /* use array to appear as a pointer */ 58 | memcpy(block, buffer, 64); 59 | #else 60 | /* The following had better never be used because it causes the 61 | * pointer-to-const buffer to be cast into a pointer to non-const. 62 | * And the result is written through. I threw a "const" in, hoping 63 | * this will cause a diagnostic. 64 | */ 65 | CHAR64LONG16* block = (const CHAR64LONG16*)buffer; 66 | #endif 67 | /* Copy context->state[] to working vars */ 68 | a = state[0]; 69 | b = state[1]; 70 | c = state[2]; 71 | d = state[3]; 72 | e = state[4]; 73 | /* 4 rounds of 20 operations each. Loop unrolled. */ 74 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 75 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 76 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 77 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 78 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 79 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 80 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 81 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 82 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 83 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 84 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 85 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 86 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 87 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 88 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 89 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 90 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 91 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 92 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 93 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 94 | /* Add the working vars back into context.state[] */ 95 | state[0] += a; 96 | state[1] += b; 97 | state[2] += c; 98 | state[3] += d; 99 | state[4] += e; 100 | /* Wipe variables */ 101 | a = b = c = d = e = 0; 102 | #ifdef SHA1HANDSOFF 103 | memset(block, '\0', sizeof(block)); 104 | #endif 105 | } 106 | 107 | 108 | /* SHA1Init - Initialize new context */ 109 | 110 | void SHA1Init(SHA1_CTX* context) 111 | { 112 | /* SHA1 initialization constants */ 113 | context->state[0] = 0x67452301; 114 | context->state[1] = 0xEFCDAB89; 115 | context->state[2] = 0x98BADCFE; 116 | context->state[3] = 0x10325476; 117 | context->state[4] = 0xC3D2E1F0; 118 | context->count[0] = context->count[1] = 0; 119 | } 120 | 121 | 122 | /* Run your data through this. */ 123 | 124 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) 125 | { 126 | uint32_t i, j; 127 | 128 | j = context->count[0]; 129 | if ((context->count[0] += len << 3) < j) 130 | context->count[1]++; 131 | context->count[1] += (len>>29); 132 | j = (j >> 3) & 63; 133 | if ((j + len) > 63) { 134 | memcpy(&context->buffer[j], data, (i = 64-j)); 135 | SHA1Transform(context->state, context->buffer); 136 | for ( ; i + 63 < len; i += 64) { 137 | SHA1Transform(context->state, &data[i]); 138 | } 139 | j = 0; 140 | } 141 | else i = 0; 142 | memcpy(&context->buffer[j], &data[i], len - i); 143 | } 144 | 145 | 146 | /* Add padding and return the message digest. */ 147 | 148 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 149 | { 150 | unsigned i; 151 | unsigned char finalcount[8]; 152 | unsigned char c; 153 | 154 | #if 0 /* untested "improvement" by DHR */ 155 | /* Convert context->count to a sequence of bytes 156 | * in finalcount. Second element first, but 157 | * big-endian order within element. 158 | * But we do it all backwards. 159 | */ 160 | unsigned char *fcp = &finalcount[8]; 161 | 162 | for (i = 0; i < 2; i++) 163 | { 164 | uint32_t t = context->count[i]; 165 | int j; 166 | 167 | for (j = 0; j < 4; t >>= 8, j++) 168 | *--fcp = (unsigned char) t; 169 | } 170 | #else 171 | for (i = 0; i < 8; i++) { 172 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 173 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 174 | } 175 | #endif 176 | c = 0200; 177 | SHA1Update(context, &c, 1); 178 | while ((context->count[0] & 504) != 448) { 179 | c = 0000; 180 | SHA1Update(context, &c, 1); 181 | } 182 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 183 | for (i = 0; i < 20; i++) { 184 | digest[i] = (unsigned char) 185 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 186 | } 187 | /* Wipe variables */ 188 | memset(context, '\0', sizeof(*context)); 189 | memset(&finalcount, '\0', sizeof(finalcount)); 190 | } 191 | /* ================ end of sha1.c ================ */ 192 | 193 | #if 0 194 | #define BUFSIZE 4096 195 | 196 | int 197 | main(int argc, char **argv) 198 | { 199 | SHA1_CTX ctx; 200 | unsigned char hash[20], buf[BUFSIZE]; 201 | int i; 202 | 203 | for(i=0;i 5 | 6 | /* ================ sha1.h ================ */ 7 | /* 8 | SHA-1 in C 9 | By Steve Reid 10 | 100% Public Domain 11 | */ 12 | 13 | typedef struct { 14 | uint32_t state[5]; 15 | uint32_t count[2]; 16 | unsigned char buffer[64]; 17 | } SHA1_CTX; 18 | 19 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); 20 | void SHA1Init(SHA1_CTX* context); 21 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); 22 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "php_tarantool.h" 2 | 3 | #include "utils.h" 4 | 5 | #include 6 | 7 | const char *tutils_op_to_string(zval *obj) { 8 | zend_uchar type = Z_TYPE_P(obj); 9 | switch(type) { 10 | case(IS_UNDEF): 11 | return "UNDEF"; 12 | case(IS_NULL): 13 | return "NULL"; 14 | case(IS_FALSE): 15 | return "FALSE"; 16 | case(IS_TRUE): 17 | return "TRUE"; 18 | case(IS_LONG): 19 | return "LONG"; 20 | case(IS_DOUBLE): 21 | return "DOUBLE"; 22 | case(IS_STRING): 23 | return "STRING"; 24 | case(IS_ARRAY): 25 | return "ARRAY"; 26 | case(IS_OBJECT): 27 | return "OBJECT"; 28 | case(IS_RESOURCE): 29 | return "RESOURCE"; 30 | case(IS_REFERENCE): 31 | return "REFERENCE"; 32 | #if PHP_VERSION_ID < 70300 33 | case(IS_CONSTANT): 34 | return "CONSTANT"; 35 | #endif 36 | case(IS_CONSTANT_AST): 37 | return "CONSTANT_AST"; 38 | case(IS_CALLABLE): 39 | return "CALLABLE"; 40 | default: 41 | return "UNKNOWN"; 42 | } 43 | } 44 | 45 | void tutils_hexdump_base (FILE *ostream, char *desc, const char *addr, size_t len) { 46 | size_t i; 47 | unsigned char buff[17]; 48 | const unsigned char *pc = (const unsigned char *)addr; 49 | 50 | if (desc != NULL) { 51 | fprintf(ostream, "%s:\n", desc); 52 | } 53 | 54 | for (i = 0; i < len; i++) { 55 | if (i % 16 == 0) { 56 | if (i != 0) fprintf(ostream, " %s\n", buff); 57 | fprintf(ostream, " %04zx ", i); 58 | } 59 | 60 | fprintf(ostream, " %02x", *pc); 61 | 62 | if ((*pc < 0x20) || (*pc > 0x7e)) 63 | buff[i % 16] = '.'; 64 | else 65 | buff[i % 16] = *pc; 66 | buff[(i % 16) + 1] = '\0'; 67 | ++pc; 68 | } 69 | 70 | while (i++ % 16 != 0) fprintf(ostream, " "); 71 | 72 | fprintf(ostream, " %s\n\n", buff); 73 | } 74 | 75 | void tutils_hexdump (char *desc, const char *addr, size_t len) { 76 | return tutils_hexdump_base(stdout, desc, addr, len); 77 | } 78 | 79 | void tutils_hexdump_zs (char *desc, zend_string *val) { 80 | return tutils_hexdump(desc, ZSTR_VAL(val), ZSTR_LEN(val)); 81 | } 82 | 83 | void tutils_hexdump_ss (char *desc, smart_string *val) { 84 | return tutils_hexdump(desc, val->c, val->len); 85 | } 86 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_TNT_UTILS_H 2 | #define PHP_TNT_UTILS_H 3 | 4 | const char *tutils_op_to_string(zval *obj); 5 | void tutils_hexdump_base (FILE *ostream, char *desc, const char *addr, size_t len); 6 | void tutils_hexdump (char *desc, const char *addr, size_t len); 7 | void tutils_hexdump_zs (char *desc, zend_string *val); 8 | void tutils_hexdump_ss (char *desc, smart_string *val); 9 | 10 | #endif /* PHP_TNT_UTILS_H */ 11 | -------------------------------------------------------------------------------- /test-run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import shlex 6 | import shutil 7 | import subprocess 8 | 9 | import time 10 | 11 | from lib.tarantool_server import TarantoolServer 12 | 13 | from pprint import pprint 14 | 15 | def read_popen(cmd): 16 | path = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) 17 | path.wait() 18 | return path.stdout.read().decode() 19 | 20 | def read_popen_config(cmd): 21 | cmd = os.environ.get('PHP_CONFIG', 'php-config') + ' ' + cmd 22 | return read_popen(cmd) 23 | 24 | def find_php_bin(): 25 | path = read_popen('which php').strip() 26 | if (path.find('phpenv') != -1): 27 | version = read_popen('phpenv global') 28 | if (version.find('system') != -1): 29 | return '/usr/bin/php' 30 | return '~/.phpenv/versions/{0}/bin/php'.format(version.strip()) 31 | return path 32 | 33 | def find_phpunit_bin(): 34 | path = read_popen('which phpunit').strip() 35 | if not path: 36 | raise RuntimeError('Unable to find phpunit binary in PATH: ' + 37 | os.environ['PATH']) 38 | return path 39 | 40 | def prepare_env(php_ini): 41 | if os.path.isdir('var'): 42 | shutil.rmtree('var') 43 | if not os.path.isdir('var') and not os.path.exists('var'): 44 | os.mkdir('var') 45 | shutil.copy('test/shared/phpunit.xml', 'var') 46 | shutil.copy(php_ini, 'var') 47 | shutil.copy('modules/tarantool.so', 'var') 48 | 49 | def main(): 50 | path = os.path.dirname(sys.argv[0]) 51 | if not path: 52 | path = '.' 53 | os.chdir(path) 54 | if '--prepare' in sys.argv: 55 | prepare_env('test/shared/tarantool-1.ini') 56 | exit(0) 57 | srv = None 58 | srv = TarantoolServer() 59 | srv.script = 'test/shared/box.lua' 60 | srv.start() 61 | test_cwd = os.path.dirname(srv.vardir) 62 | try: 63 | shutil.copy('test/shared/phpunit.xml', os.path.join(test_cwd, 'phpunit.xml')) 64 | shutil.copy('modules/tarantool.so', test_cwd) 65 | try: 66 | test_lib_path = find_phpunit_bin() 67 | except RuntimeError as e: 68 | print(str(e)) 69 | return 1 70 | 71 | version = read_popen_config('--version').strip(' \n\t') + '.' 72 | version1 = read_popen_config('--extension-dir').strip(' \n\t') 73 | version += '\n' + ('With' if version1.find('non-zts') == -1 else 'Without') + ' ZTS' 74 | version += '\n' + ('With' if version1.find('no-debug') == -1 else 'Without') + ' Debug' 75 | print('Running against ' + version) 76 | 77 | for php_ini in [ 78 | 'test/shared/tarantool-1.ini', 79 | # Temporary disabled testing configuration with 80 | # persistent connections due to gh-113 ('Receiving 81 | # trash from persistent connect when there was timeout 82 | # before'). 83 | # 84 | # 'test/shared/tarantool-2.ini', 85 | # 'test/shared/tarantool-3.ini' 86 | ]: 87 | cmd = '' 88 | shutil.copy(php_ini, os.path.join(test_cwd, 'tarantool.ini')) 89 | if '--flags' in sys.argv: 90 | os.environ['ZEND_DONT_UNLOAD_MODULES'] = '1' 91 | os.environ['USE_ZEND_ALLOC'] = '0' 92 | os.environ['MALLOC_CHECK_'] = '3' 93 | if '--valgrind' in sys.argv: 94 | cmd = cmd + 'valgrind --leak-check=full --show-leak-kinds=all --log-file=' 95 | cmd = cmd + os.path.basename(php_ini).split('.')[0] + '.out ' 96 | cmd = cmd + '--suppressions=test/shared/valgrind.sup ' 97 | cmd = cmd + '--keep-stacktraces=alloc-and-free' 98 | cmd = cmd + ' --freelist-vol=2000000000 ' 99 | cmd = cmd + '--malloc-fill=0 --free-fill=0 ' 100 | cmd = cmd + '--num-callers=50 ' + find_php_bin() 101 | cmd = cmd + ' -c tarantool.ini {0}'.format(test_lib_path) 102 | elif '--gdb' in sys.argv: 103 | cmd = cmd + 'gdb {0} --ex '.format(find_php_bin()) 104 | cmd = cmd + '"set args -c tarantool.ini {0}"'.format(test_lib_path) 105 | elif '--lldb' in sys.argv: 106 | cmd = cmd + 'lldb -- {0} -c tarantool.ini {1}'.format(find_php_bin(), test_lib_path) 107 | elif '--strace' in sys.argv: 108 | cmd = cmd + 'strace ' + find_php_bin() 109 | cmd = cmd + ' -c tarantool.ini {0}'.format(test_lib_path) 110 | elif '--dtruss' in sys.argv: 111 | cmd = cmd + 'sudo dtruss ' + find_php_bin() 112 | cmd = cmd + ' -c tarantool.ini {0}'.format(test_lib_path) 113 | else: 114 | print(find_php_bin()) 115 | cmd = '{0} -c tarantool.ini {1}'.format(find_php_bin(), test_lib_path) 116 | print(cmd) 117 | 118 | print('Running "%s" with "%s"' % (cmd, php_ini)) 119 | proc = subprocess.Popen(cmd, shell=True, cwd=test_cwd) 120 | rv = proc.wait() 121 | if rv != 0: 122 | print('Error') 123 | return -1 124 | if '--gdb' in sys.argv or '--lldb' in sys.argv: 125 | return -1 126 | 127 | finally: 128 | a = [ 129 | os.path.join(test_cwd, 'tarantool.ini'), 130 | os.path.join(test_cwd, 'phpunit.xml'), 131 | os.path.join(test_cwd, 'tarantool.so'), 132 | ] 133 | for elem in a: 134 | if os.path.exists(elem): 135 | os.remove(elem) 136 | 137 | return 0 138 | 139 | exit(main()) 140 | -------------------------------------------------------------------------------- /test.all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -exu # Strict shell (w/o -o pipefail) 4 | 5 | php_version_list="7.0 7.1 7.2 7.3 7.4" 6 | tarantool_version_list="1.6 1.7 1.9 1.10 2.1 2.2 2.3 2.4 2.5" 7 | 8 | # Disable tarantool-1.6 and 1.7 on php 7.1-7.4, because 9 | # php-7.[1-4]-cli docker images are based on Debian Buster, but we 10 | # have no tarantool-1.[6-7] packages for this distribution. 11 | exceptions=" 12 | php-7.1-tarantool-1.6 13 | php-7.1-tarantool-1.7 14 | php-7.2-tarantool-1.6 15 | php-7.2-tarantool-1.7 16 | php-7.3-tarantool-1.6 17 | php-7.3-tarantool-1.7 18 | php-7.4-tarantool-1.6 19 | php-7.4-tarantool-1.7 20 | " 21 | 22 | for php_version in $php_version_list; do 23 | for tarantool_version in $tarantool_version_list; do 24 | echo "======================================" 25 | echo "PHP_VERSION=${php_version} TARANTOOL_VERSION=${tarantool_version}" 26 | echo "======================================" 27 | 28 | # Skip configurations from exceptions list. 29 | conf="php-${php_version}-tarantool-${tarantool_version}" 30 | if [ -z "${exceptions##*${conf}*}" ]; then 31 | echo "*** Skip the configuration ***" 32 | continue 33 | fi 34 | 35 | docker run \ 36 | --volume $(realpath .):/tarantool-php \ 37 | --workdir /tarantool-php \ 38 | --env TARANTOOL_VERSION=${tarantool_version} \ 39 | --rm \ 40 | php:${php_version}-cli \ 41 | sh -c 'make clean; ./test.sh' 42 | done 43 | done 44 | -------------------------------------------------------------------------------- /test.pkg.all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -exu # Strict shell (w/o -o pipefail) 4 | 5 | distros=" 6 | el:8 7 | fedora:25 8 | fedora:26 9 | fedora:27 10 | fedora:28 11 | fedora:29 12 | fedora:30 13 | fedora:31 14 | debian:stretch 15 | debian:buster 16 | ubuntu:xenial 17 | ubuntu:bionic 18 | ubuntu:eoan 19 | ubuntu:focal 20 | " 21 | 22 | if ! type packpack; then 23 | echo "Unable to find packpack" 24 | exit 1 25 | fi 26 | 27 | for distro in $distros; do 28 | export OS="${distro%%:*}" 29 | export DIST="${distro#*:}" 30 | if [ "${OS}" = "el" ]; then 31 | export OS=centos 32 | fi 33 | 34 | rm -rf build 35 | packpack 36 | 37 | docker run \ 38 | --volume "$(realpath .):/tarantool-php" \ 39 | --workdir /tarantool-php \ 40 | --rm \ 41 | "${OS}:${DIST}" \ 42 | ./test.pkg.sh 43 | done 44 | -------------------------------------------------------------------------------- /test.pkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -exu # Strict shell (w/o -o pipefail) 4 | 5 | if type dnf; then 6 | PM_SYNC='dnf makecache' 7 | PM_INSTALL_NAME='dnf -qy install' 8 | PM_INSTALL_FILE='dnf -qy install' 9 | PKG_GLOB='php-tarantool-*.x86_64.rpm' 10 | elif type yum; then 11 | PM_SYNC='yum makecache' 12 | PM_INSTALL_NAME='yum -qy install' 13 | PM_INSTALL_FILE='yum -qy install' 14 | PKG_GLOB='php-tarantool-*.x86_64.rpm' 15 | elif type apt-get; then 16 | PM_SYNC='apt-get update' 17 | PM_INSTALL_NAME='apt-get -qy install' 18 | PM_INSTALL_FILE='dpkg -i' 19 | PKG_GLOB='php7.?-tarantool*_amd64.deb' 20 | # Prevent packages like tzdata from asking configuration 21 | # parameters interactively. 22 | # See https://github.com/packpack/packpack/issues/7 23 | export DEBIAN_FRONTEND=noninteractive 24 | else 25 | echo "No suitable package manager found" 26 | exit 1 27 | fi 28 | 29 | # Update available packages list. 30 | ${PM_SYNC} 31 | 32 | # Install php interpreter. 33 | ${PM_INSTALL_NAME} php 34 | 35 | # Install php-tarantool package. 36 | ${PM_INSTALL_FILE} build/${PKG_GLOB} 37 | 38 | # A kind of very minimaliztic test. Verify that Tarantool instance 39 | # can be created. It means that we at least install the dynamic 40 | # library and php configuration file into right places. 41 | php -r "new Tarantool('127.0.0.1', 3301);" 42 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -exu # Strict shell (w/o -o pipefail) 4 | 5 | TARANTOOL_VERSION=${TARANTOOL_VERSION:-1.10} 6 | 7 | if [ "$(id -u)" = "0" ]; then 8 | SUDO="" 9 | else 10 | SUDO="sudo" 11 | fi 12 | 13 | ${SUDO} apt-get update > /dev/null 14 | 15 | if [ -z "$(which gpg)" ]; then 16 | ${SUDO} apt-get -qy install gnupg2 > /dev/null 17 | fi 18 | 19 | if [ -z "$(which lsb_release)" ]; then 20 | ${SUDO} apt-get -qy install lsb-release > /dev/null 21 | fi 22 | 23 | ${SUDO} apt-get -qy install apt-transport-https > /dev/null 24 | ${SUDO} apt-get -qy install python-yaml > /dev/null 25 | 26 | OS="$(lsb_release --id --short | tr '[:upper:]' '[:lower:]')" # debian or ubuntu 27 | DIST="$(lsb_release --codename --short)" # stretch, xenial, ... 28 | 29 | # Setup tarantool repository. 30 | curl https://download.tarantool.org/tarantool/${TARANTOOL_VERSION}/gpgkey | \ 31 | ${SUDO} apt-key add - 32 | ${SUDO} rm -f /etc/apt/sources.list.d/*tarantool*.list 33 | ${SUDO} tee /etc/apt/sources.list.d/tarantool_${TARANTOOL_VERSION}.list <<- EOF 34 | deb https://download.tarantool.org/tarantool/${TARANTOOL_VERSION}/${OS}/ ${DIST} main 35 | EOF 36 | ${SUDO} apt-get update > /dev/null 37 | 38 | # Install tarantool executable and headers. 39 | # 40 | # Explicit version is necessary to install a lower tarantool 41 | # version from our repository when a system have a higher version 42 | # in default repositories. Say, Debian Stretch have tarantool-1.7 43 | # that prevents tarantool-1.6 from being installed from our repo 44 | # if we would not hold the version. 45 | ${SUDO} apt-get -qy install "tarantool=${TARANTOOL_VERSION}*" \ 46 | "tarantool-dev=${TARANTOOL_VERSION}*" > /dev/null 47 | tarantool --version 48 | 49 | # Ensure we got the requested tarantool version. 50 | TARANTOOL_VERSION_PATTERN='^Tarantool \([0-9]\+\.[0-9]\+\)\..*$' 51 | ACTUAL_TARANTOOL_VERSION="$(tarantool --version | head -n 1 | \ 52 | sed "s/${TARANTOOL_VERSION_PATTERN}/\\1/")" 53 | if [ "${ACTUAL_TARANTOOL_VERSION}" != "${TARANTOOL_VERSION}" ]; then 54 | echo "Requested tarantool-${TARANTOOL_VERSION}, got" \ 55 | "tarantool-${ACTUAL_TARANTOOL_VERSION}" 56 | exit 1 57 | fi 58 | 59 | # Determine PHP interpreter version. 60 | PHP_VERSION_PATTERN='^PHP \([0-9]\+\.[0-9]\+\)\.[0-9]\+ .*$' 61 | PHP_VERSION="$(php --version | head -n 1 | sed "s/${PHP_VERSION_PATTERN}/\\1/")" 62 | 63 | # Choose maximal phpunit version supported by installed PHP 64 | # interpreter. 65 | # 66 | # https://phpunit.de/supported-versions.html 67 | case "${PHP_VERSION}" in 68 | 7.0) PHPUNIT_VERSION=6 ;; 69 | 7.1) PHPUNIT_VERSION=7 ;; 70 | 7.2) PHPUNIT_VERSION=8 ;; 71 | 7.3) PHPUNIT_VERSION=9 ;; 72 | 7.4) PHPUNIT_VERSION=9 ;; 73 | *) 74 | echo "Unable to choose phpunit version for PHP ${PHP_VERSION}" 75 | exit 1 76 | ;; 77 | esac 78 | 79 | # Install phpunit. 80 | PHPUNIT_URL="https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar" 81 | PHPUNIT_DIR="/usr/local/phpunit-${PHPUNIT_VERSION}" 82 | PHPUNIT_FILE="${PHPUNIT_DIR}/phpunit" 83 | ${SUDO} mkdir -p "${PHPUNIT_DIR}" 84 | ${SUDO} curl -SsLf -o "${PHPUNIT_FILE}" "${PHPUNIT_URL}" 85 | ${SUDO} chmod a+x "${PHPUNIT_FILE}" 86 | export PATH="${PHPUNIT_DIR}:${PATH}" 87 | phpunit --version 88 | 89 | phpize && ./configure 90 | make 91 | make install 92 | /usr/bin/python test-run.py 93 | -------------------------------------------------------------------------------- /test/AssertTest.php: -------------------------------------------------------------------------------- 1 | authenticate('test', 'test'); 19 | } 20 | 21 | public static function doTearDownAfterClass() { 22 | ini_set("tarantool.request_timeout", self::$tm); 23 | } 24 | 25 | protected function doTearDown() { 26 | $tuples = self::$tarantool->select("test"); 27 | foreach($tuples as $value) 28 | self::$tarantool->delete("test", Array($value[0])); 29 | } 30 | 31 | public function test_00_timedout() { 32 | self::$tarantool->eval(" 33 | function assert_f() 34 | os.execute('sleep 1') 35 | return 0 36 | end"); 37 | try { 38 | $result = self::$tarantool->call("assert_f"); 39 | $this->assertFalse(True); 40 | } catch (TarantoolException $e) { 41 | // print($e->getMessage()); 42 | $this->assertStringContainsString( 43 | "Failed to read", $e->getMessage()); 44 | } 45 | 46 | /* We can reconnect and everything will be ok */ 47 | self::$tarantool->close(); 48 | self::$tarantool->select("test"); 49 | } 50 | 51 | /** 52 | * @doesNotPerformAssertions 53 | */ 54 | public function test_01_closed_connection() { 55 | for ($i = 0; $i < 20000; $i++) { 56 | try { 57 | self::$tarantool->call("nonexistentfunction"); 58 | } catch (TarantoolClientError $e) { 59 | continue; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/CreateTest.php: -------------------------------------------------------------------------------- 1 | connect(); 26 | $this->assertTrue($c->ping()); 27 | $c->close(); 28 | } 29 | 30 | public function test_01_create_test_ping_and_close() { 31 | $c = new Tarantool('localhost', self::$port); 32 | $c->connect(); 33 | $c->connect(); 34 | $this->assertTrue($c->ping()); 35 | $c->close(); 36 | $this->assertTrue($c->ping()); 37 | $c->close(); 38 | $c->close(); 39 | } 40 | 41 | /** 42 | * @doesNotPerformAssertions 43 | */ 44 | public function test_01_01_double_disconnect() { 45 | $a = new Tarantool('localhost', self::$port); 46 | $a->disconnect(); 47 | $a->disconnect(); 48 | } 49 | 50 | public function test_02_create_error_host() { 51 | $c = new Tarantool('very_bad_host'); 52 | 53 | $this->expectException(TarantoolException::class); 54 | $this->expectExceptionMessageMatches( 55 | '/Name or service not known' . 56 | '|nodename nor servname provided' . 57 | '|Temporary failure in name resolution/'); 58 | $c->connect(); 59 | } 60 | 61 | public function test_03_00_create_error_port() { 62 | $c = new Tarantool('127.0.0.1', 65500); 63 | 64 | $this->expectException(TarantoolException::class); 65 | $this->expectExceptionMessageMatches( 66 | '/Connection refused|Network is unreachable/'); 67 | $c->connect(); 68 | } 69 | 70 | public function test_03_01_create_error_port() { 71 | $this->expectException(TarantoolException::class); 72 | $this->expectExceptionMessage('Invalid primary port value'); 73 | new Tarantool('localhost', 123456); 74 | } 75 | 76 | public function test_04_create_many_conns() { 77 | $a = 1; 78 | while ($a < 10) { 79 | $this->assertTrue((new Tarantool('127.0.0.1', self::$port))->ping()); 80 | $a++; 81 | } 82 | } 83 | 84 | public function test_05_flush_authenticate() { 85 | $c = new Tarantool('localhost', self::$port); 86 | $c->connect(); 87 | $this->assertTrue($c->ping()); 88 | $c->authenticate('test', 'test'); 89 | $c->select("test"); 90 | $c->flushSchema(); 91 | $c->select("test"); 92 | $c->flush_schema(); 93 | } 94 | 95 | public function test_05_flush_construct() { 96 | $c = new Tarantool('localhost', self::$port, 'test', 'test'); 97 | $this->assertTrue($c->ping()); 98 | $c->select("test"); 99 | $c->flushSchema(); 100 | $c->select("test"); 101 | $c->flush_schema(); 102 | } 103 | 104 | public function test_06_bad_credentials() { 105 | $c = new Tarantool('localhost', self::$port); 106 | $c->connect(); 107 | $this->assertTrue($c->ping()); 108 | 109 | $this->expectException(TarantoolClientError::class); 110 | $this->expectExceptionMessageMatches( 111 | '/(Incorrect password supplied for user)|(User not found or supplied credentials are invalid)/'); 112 | $c->authenticate('test', 'bad_password'); 113 | } 114 | 115 | public function test_07_bad_guest_credentials() { 116 | $c = new Tarantool('localhost', self::$port); 117 | $c->connect(); 118 | $this->assertTrue($c->ping()); 119 | 120 | $this->expectException(TarantoolClientError::class); 121 | $this->expectExceptionMessageMatches( 122 | '/(Incorrect password supplied for user)|(User not found or supplied credentials are invalid)/'); 123 | $c->authenticate('guest', 'guest'); 124 | } 125 | 126 | /** 127 | * Comment this, since behaviour of authentication with 'empty password' has changed 128 | public function test_07_01_bad_guest_credentials() { 129 | $c = new Tarantool('localhost', self::$port); 130 | $c->connect(); 131 | $this->assertTrue($c->ping()); 132 | 133 | $this->expectException(TarantoolClientError::class); 134 | $this->expectExceptionMessage( 135 | 'Incorrect password supplied for user'); 136 | $c->authenticate('guest', ''); 137 | } 138 | */ 139 | 140 | /** 141 | * @dataProvider provideGoodCredentials 142 | */ 143 | public function test_08_good_credentials_construct($username, $password = null) { 144 | if (func_num_args() === 1) { 145 | $c = new Tarantool('localhost', self::$port, $username); 146 | } else { 147 | $c = new Tarantool('localhost', self::$port, $username, $password); 148 | } 149 | $this->assertTrue($c->ping()); 150 | } 151 | 152 | public function test_09_inheritance() { 153 | $c = new class ('localhost', self::$port) extends Tarantool { 154 | public $property = 42; 155 | }; 156 | $this->assertSame(42, $c->property); 157 | } 158 | 159 | public static function provideGoodCredentials() 160 | { 161 | return [ 162 | ['guest'], 163 | ['guest', null], 164 | ]; 165 | } 166 | 167 | public function test_10_zero_retry_exception() { 168 | /* A connection to call iproto_connect_count(). */ 169 | $tarantool = new Tarantool('localhost', self::$port, 'test', 'test'); 170 | $iproto_connect_count_before = 171 | $tarantool->call('iproto_connect_count')[0][0]; 172 | 173 | $saved_retry_count = ini_get('tarantool.retry_count'); 174 | ini_set('tarantool.retry_count', 0); 175 | 176 | $exp_err = 'tarantool.retry_count should be 1 or above'; 177 | try { 178 | $c = new Tarantool('localhost', self::$port); 179 | $c->connect(); 180 | $this->assertFalse(true); 181 | } catch (Exception $e) { 182 | $this->assertInstanceOf(TarantoolException::class, $e); 183 | $this->assertEquals($exp_err, $e->getMessage()); 184 | } finally { 185 | ini_set('tarantool.retry_count', $saved_retry_count); 186 | } 187 | 188 | /* Verify that no connection attempts were performed. */ 189 | $iproto_connect_count_after = 190 | $tarantool->call('iproto_connect_count')[0][0]; 191 | $this->assertEquals($iproto_connect_count_before, 192 | $iproto_connect_count_after); 193 | } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /test/MockTest.php: -------------------------------------------------------------------------------- 1 | createMock(Tarantool::class); 12 | $tnt->expects($this->once())->method('ping'); 13 | $tnt->ping(); 14 | } 15 | 16 | public function testDoo() 17 | { 18 | try { 19 | $port = testHelpers::getTarantoolPort(); 20 | (new Tarantool('localhost', $port))->select('_vindex', [], 'name'); 21 | $this->assertFalse(True); 22 | } catch (Exception $e) { 23 | $this->assertTrue(True); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/MsgPackTest.php: -------------------------------------------------------------------------------- 1 | ping(); 18 | } 19 | 20 | public function test_00_msgpack_call() { 21 | $resp = self::$tarantool->call('test_4', [ 22 | '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 23 | 'browser_stats_first_session_hits' => 1 24 | ] 25 | ], ['call_16' => true]); 26 | $this->assertEquals($resp[0][0], 2); 27 | $resp = self::$tarantool->call('test_4', [ 28 | '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 29 | 'browser_stats_first_session_hit' => 1 30 | ] 31 | ], ['call_16' => true]); 32 | $this->assertEquals($resp[0][0], 2); 33 | 34 | $check_call_17 = self::$tarantool->call('tarantool_version_at_least', 35 | array(1,7,2,0)); 36 | if ($check_call_17[0][0]) { 37 | $resp = self::$tarantool->call('test_4', [ 38 | '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 39 | 'browser_stats_first_session_hits' => 1 40 | ] 41 | ], ['call_16' => false]); 42 | $this->assertEquals($resp[0], 2); 43 | $resp = self::$tarantool->call('test_4', [ 44 | '4TL2tLIXqMqyGQm_kiE7mRrS96I5E8nqU', 'B627', 0, [ 45 | 'browser_stats_first_session_hit' => 1 46 | ] 47 | ], ['call_16' => false]); 48 | $this->assertEquals($resp[0], 2); 49 | } 50 | } 51 | 52 | public function test_01_msgpack_array_key() { 53 | $this->expectException(TarantoolException::class); 54 | $this->expectExceptionMessage('Bad key type for PHP Array'); 55 | self::$tarantool->select("msgpack", array(2)); 56 | } 57 | 58 | public function test_02_msgpack_float_key() { 59 | $this->expectException(TarantoolException::class); 60 | $this->expectExceptionMessage('Bad key type for PHP Array'); 61 | self::$tarantool->select("msgpack", array(1)); 62 | } 63 | 64 | public function test_03_msgpack_array_of_float_as_key() { 65 | $this->expectException(TarantoolException::class); 66 | $this->expectExceptionMessage('Bad key type for PHP Array'); 67 | self::$tarantool->select("msgpack", array(3)); 68 | } 69 | 70 | /** 71 | * @doesNotPerformAssertions 72 | */ 73 | public function test_04_msgpack_integer_keys_arrays() { 74 | self::$tarantool->replace("msgpack", array(4, 75 | "Integer keys causing server to error", 76 | array(2 => 'maria', 5 => 'db') 77 | ) 78 | ); 79 | } 80 | 81 | public function test_05_msgpack_string_keys() { 82 | self::$tarantool->replace("msgpack", array(5, 83 | "String keys in response forbids client to take values by keys", 84 | array(2 => 'maria', 5 => 'db', 'lol' => 'lal') 85 | ) 86 | ); 87 | $resp = self::$tarantool->select("msgpack", array(5)); 88 | $this->assertEquals($resp[0][2]['lol'], 'lal'); 89 | $this->assertTrue(True); 90 | $resp = self::$tarantool->select("msgpack", array(6)); 91 | $this->assertEquals($resp[0][2]['megusta'], array(1, 2, 3)); 92 | $this->assertTrue(True); 93 | } 94 | 95 | /** 96 | * @doesNotPerformAssertions 97 | */ 98 | public function test_06_msgpack_array_reference() { 99 | $data = array('key1' => 'value1'); 100 | $link = &$data['key1']; 101 | self::$tarantool->call('test_4', [$data], ['call_16' => true]); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /test/PhpUnitCompat.php: -------------------------------------------------------------------------------- 1 | 54 | * | } 55 | * | 56 | * | public static function doTearDownAfterClass() { 57 | * | <...> 58 | * | } 59 | * | 60 | * | protected function doSetUp() { 61 | * | <...> 62 | * | } 63 | * | 64 | * | protected function doTearDown() { 65 | * | <...> 66 | * | } 67 | * | } 68 | * 69 | * [1]: https://phpunit.de/announcements/phpunit-7.html 70 | * 71 | * assertContains() for strings 72 | * ---------------------------- 73 | * 74 | * phpunit-8 warns about using of assertContains() for search a 75 | * substring in a string. Add `use TestCaseCompat;` to a TestCase 76 | * derived class and use assertStringContainsString() on any 77 | * phpunit-6+ version. 78 | * 79 | * expectExceptionMessageRegExp() removal 80 | * -------------------------------------- 81 | * 82 | * phpunit-8 and newer provides the same function under 83 | * expectExceptionMessageMatches() name. phpunit-9 removes the old 84 | * expectExceptionMessageRegExp() alias. 85 | */ 86 | 87 | use PHPUnit\Framework\TestCase; 88 | 89 | $testCaseRef = new ReflectionClass(TestCase::class); 90 | 91 | /* 92 | * SetUpTearDownTraitDefaultDoMethods (internal). 93 | * 94 | * The common code to use in SetUpTearDownTrait traits on both 95 | * phpunit-6 and phpunit-7+. 96 | */ 97 | trait SetUpTearDownTraitDefaultDoMethods 98 | { 99 | private static function doSetUpBeforeClass() 100 | { 101 | parent::setUpBeforeClass(); 102 | } 103 | 104 | private static function doTearDownAfterClass() 105 | { 106 | parent::tearDownAfterClass(); 107 | } 108 | 109 | private function doSetUp() 110 | { 111 | parent::setUp(); 112 | } 113 | 114 | private function doTearDown() 115 | { 116 | parent::tearDown(); 117 | } 118 | } 119 | 120 | /* 121 | * SetUpTearDownTrait (private). 122 | * 123 | * This trait allow to overcome the problem that php-7.0 does not 124 | * support `void` return type declaration, while phpunit-7 (which 125 | * supports php-7.1+) requires them for certain set of methods. 126 | * 127 | * The idea is borrowed from Symfony's PHPUnit Bridge, see [1]. 128 | * 129 | * [1]: https://symfony.com/doc/current/components/phpunit_bridge.html#removing-the-void-return-type 130 | */ 131 | if ($testCaseRef->getMethod('setUp')->hasReturnType()) { 132 | /* phpunit-7 and newer */ 133 | trait SetUpTearDownTrait 134 | { 135 | use SetUpTearDownTraitDefaultDoMethods; 136 | 137 | public static function setUpBeforeClass(): void 138 | { 139 | self::doSetUpBeforeClass(); 140 | } 141 | 142 | public static function tearDownAfterClass(): void 143 | { 144 | self::doTearDownAfterClass(); 145 | } 146 | 147 | protected function setUp(): void 148 | { 149 | self::doSetUp(); 150 | } 151 | 152 | protected function tearDown(): void 153 | { 154 | self::doTearDown(); 155 | } 156 | } 157 | } else { 158 | /* phpunit-6 */ 159 | trait SetUpTearDownTrait 160 | { 161 | use SetUpTearDownTraitDefaultDoMethods; 162 | 163 | public static function setUpBeforeClass() 164 | { 165 | self::doSetUpBeforeClass(); 166 | } 167 | 168 | public static function tearDownAfterClass() 169 | { 170 | self::doTearDownAfterClass(); 171 | } 172 | 173 | protected function setUp() 174 | { 175 | self::doSetUp(); 176 | } 177 | 178 | protected function tearDown() 179 | { 180 | self::doTearDown(); 181 | } 182 | } 183 | } 184 | 185 | /* 186 | * AssertStringContainsStringTrait (private). 187 | * 188 | * phpunit-6 does not contain assertStringContainsString() method, 189 | * while phpunit-8 warns about using assertContains() for search a 190 | * substring in a string. 191 | * 192 | * This trait adds assertStringContainsString() method for 193 | * phpunit-6. 194 | */ 195 | if ($testCaseRef->hasMethod('assertStringContainsString')) { 196 | trait AssertStringContainsStringTrait 197 | { 198 | /* Nothing to define. */ 199 | } 200 | } else { 201 | trait AssertStringContainsStringTrait 202 | { 203 | public static function assertStringContainsString( 204 | $needle, $haystack, $message = '') 205 | { 206 | self::assertContains($needle, $haystack, $message); 207 | } 208 | } 209 | } 210 | 211 | /* 212 | * ExpectExceptionMessageMatchesTrait (private). 213 | * 214 | * phpunit-8 provides the new name for 215 | * expectExceptionMessageRegExp() method: 216 | * expectExceptionMessageMatches(). phpunit-9 removes the old name. 217 | * 218 | * This trait adds expectExceptionMessageMatches() method for 219 | * phpunit-6 and phpunit-7. 220 | */ 221 | if ($testCaseRef->hasMethod('expectExceptionMessageMatches')) { 222 | trait ExpectExceptionMessageMatchesTrait { 223 | /* Nothing to define. */ 224 | } 225 | } else { 226 | trait ExpectExceptionMessageMatchesTrait { 227 | public function expectExceptionMessageMatches($regularExpression) { 228 | self::expectExceptionMessageRegExp($regularExpression); 229 | } 230 | } 231 | } 232 | 233 | /* 234 | * AssertMatchesRegularExpressionTrait (private). 235 | * 236 | * phpunit-8 provides the new name for the 237 | * assertRegExp() method: 238 | * assertMatchesRegularExpression(). phpunit-10 removes the old name. 239 | * 240 | * This trait adds assertMatchesRegularExpression() method for 241 | * phpunit-6, phpunit-7, phpunit-8 and phpunit-9. 242 | */ 243 | if ($testCaseRef->hasMethod('assertMatchesRegularExpression')) { 244 | trait AssertMatchesRegularExpressionTrait { 245 | /* Nothing to define. */ 246 | } 247 | } else { 248 | trait AssertMatchesRegularExpressionTrait { 249 | public function assertMatchesRegularExpression($pattern, $string, $message = '') { 250 | self::assertRegExp($pattern, $string, $message); 251 | } 252 | } 253 | } 254 | 255 | /* 256 | * TestCaseCompat (public). 257 | * 258 | * This trait accumulates all hacks defined above. 259 | */ 260 | trait TestCaseCompat 261 | { 262 | use SetUpTearDownTrait; 263 | use AssertStringContainsStringTrait; 264 | use ExpectExceptionMessageMatchesTrait; 265 | use AssertMatchesRegularExpressionTrait; 266 | } 267 | -------------------------------------------------------------------------------- /test/RandomTest.php: -------------------------------------------------------------------------------- 1 | ping(); 27 | } 28 | 29 | public function test_01_random_big_response() { 30 | for ($i = 5000; $i < 100000; $i += 1000) { 31 | $request = "do return '" . generateRandomString($i) . "' end"; 32 | self::$tarantool->evaluate($request); 33 | } 34 | $this->assertTrue(True); 35 | } 36 | 37 | public function test_02_very_big_response() { 38 | $request = "do return '" . str_repeat('x', 1024 * 1024 * 1) . "' end"; 39 | self::$tarantool->evaluate($request); 40 | $this->assertTrue(True); 41 | } 42 | 43 | public function test_03_another_big_response() { 44 | for ($i = 100; $i <= 5200; $i += 100) { 45 | $result = self::$tarantool->call('test_5', array($i), 46 | array('call_16' => true)); 47 | $this->assertEquals($i, count($result)); 48 | } 49 | 50 | $check_call_17 = self::$tarantool->call('tarantool_version_at_least', 51 | array(1,7,2,0)); 52 | if ($check_call_17[0][0]) { 53 | for ($i = 100; $i <= 5200; $i += 100) { 54 | $result = self::$tarantool->call('test_5', array($i), 55 | array('call_16' => false)); 56 | $this->assertEquals($i, count($result[0])); 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * @doesNotPerformAssertions 63 | */ 64 | public function test_04_get_strange_response() { 65 | self::$tarantool->select("_schema", "12345"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/TestHelpers.php: -------------------------------------------------------------------------------- 1 | major then return true end 28 | 29 | if _TARANTOOL_MINOR < minor then return false end 30 | if _TARANTOOL_MINOR > minor then return true end 31 | 32 | if _TARANTOOL_PATCH < patch then return false end 33 | if _TARANTOOL_PATCH > patch then return true end 34 | 35 | if _TARANTOOL_REV < rev then return false end 36 | if _TARANTOOL_REV > rev then return true end 37 | 38 | return true 39 | end 40 | 41 | local compat = {} 42 | 43 | if tarantool_version_at_least(1, 7, 3, 351) then 44 | compat.log = 'log' 45 | compat.memtx_memory = 'memtx_memory' 46 | compat.memtx_memory_transform = function(v) 47 | return v 48 | end 49 | else 50 | compat.log = 'logger' 51 | compat.memtx_memory = 'slab_alloc_arena' 52 | compat.memtx_memory_transform = function(v) 53 | return v / 1024 ^ 3 54 | end 55 | end 56 | 57 | if tarantool_version_at_least(1, 7, 1, 147) then 58 | compat.unsigned = 'unsigned' 59 | compat.string = 'string' 60 | else 61 | compat.unsigned = 'NUM' 62 | compat.string = 'STR' 63 | end 64 | 65 | -- }}} 66 | 67 | box.cfg{ 68 | listen = os.getenv('PRIMARY_PORT'), 69 | log_level = 5, 70 | [compat.log] = 'tarantool.log', 71 | [compat.memtx_memory] = compat.memtx_memory_transform(400 * 1024 * 1024), 72 | } 73 | 74 | box.once('initialization', function() 75 | box.schema.user.create('test', { password = 'test' }) 76 | box.schema.user.create('test_empty', { password = '' }) 77 | box.schema.user.create('test_big', { 78 | password = '123456789012345678901234567890123456789012345678901234567890' 79 | }) 80 | box.schema.user.grant('test', 'read,write,execute', 'universe') 81 | 82 | local space = box.schema.space.create('test', { 83 | format = { 84 | { type = compat.unsigned, name = 'field1', add_field = 1 }, 85 | { type = compat.unsigned, name = 's1' }, 86 | { type = compat.string, name = 's2' }, 87 | } 88 | }) 89 | space:create_index('primary', { 90 | type = 'TREE', 91 | unique = true, 92 | parts = {1, compat.unsigned} 93 | }) 94 | space:create_index('secondary', { 95 | type = 'TREE', 96 | unique = false, 97 | parts = {2, compat.unsigned, 3, compat.string} 98 | }) 99 | 100 | local space = box.schema.space.create('msgpack') 101 | space:create_index('primary', { 102 | parts = {1, compat.unsigned} 103 | }) 104 | space:insert{1, 'float as key', { 105 | [2.7] = {1, 2, 3} 106 | }} 107 | space:insert{2, 'array as key', { 108 | [{2, 7}] = {1, 2, 3} 109 | }} 110 | space:insert{3, 'array with float key as key', { 111 | [{ 112 | [2.7] = 3, 113 | [7] = 7 114 | }] = {1, 2, 3} 115 | }} 116 | space:insert{6, 'array with string key as key', { 117 | ['megusta'] = {1, 2, 3} 118 | }} 119 | 120 | local test_hash = box.schema.space.create('test_hash') 121 | test_hash:create_index('primary', { 122 | type = 'HASH', 123 | unique = true 124 | }) 125 | test_hash:insert{1, 'hash-loc'} 126 | test_hash:insert{2, 'hash-col'} 127 | test_hash:insert{3, 'hash-olc'} 128 | 129 | local space = box.schema.space.create('pstring') 130 | space:create_index('primary', { 131 | parts = {1, compat.string} 132 | }) 133 | local yml = io.open(fio.pathjoin(fio.cwd(), "../test/shared/queue.yml")):read("*a") 134 | local tuple = yaml.decode(yml)[1] 135 | tuple[1] = "12345" 136 | box.space._schema:insert(tuple) 137 | 138 | -- gh-151: Support new _index system space format 139 | -- After tarantool-1.7.5-153-g1651fc9be the new _index format 140 | -- was introduced. When an index part uses is_nullable or 141 | -- collation parameter, then the new format will be used. 142 | if tarantool_version_at_least(1, 7, 6, 0) then 143 | local test_index_part_176 = box.schema.space.create( 144 | 'test_index_part_176', 145 | { 146 | format = { 147 | {type = compat.unsigned, name = 'f1'}, 148 | {type = compat.unsigned, name = 'f2', is_nullable = true}, 149 | {type = compat.string, name = 'f3'}, 150 | } 151 | }) 152 | 153 | test_index_part_176:create_index('primary', { 154 | parts = {{1, compat.unsigned}} 155 | }) 156 | test_index_part_176:create_index('secondary', { 157 | parts = { 158 | {2, compat.unsigned, is_nullable = true}, 159 | {3, compat.string, collation = 'unicode_ci'} 160 | } 161 | }) 162 | end 163 | end) 164 | 165 | function test_1() 166 | return true, { 167 | c = { 168 | ['106'] = {1, 1428578535}, 169 | ['2'] = {1, 1428578535} 170 | }, 171 | pc = { 172 | ['106'] = {1, 1428578535, 9243}, 173 | ['2'] = {1, 1428578535, 9243} 174 | }, 175 | s = {1, 1428578535}, 176 | u = 1428578535, 177 | v = {} 178 | }, true 179 | end 180 | 181 | function test_2() 182 | return { 183 | k2 = 'v', 184 | k1 = 'v2' 185 | } 186 | end 187 | 188 | function test_3(x, y) 189 | return x + y 190 | end 191 | 192 | function test_4(...) 193 | local args = {...} 194 | log.info('%s', json.encode(args)) 195 | return 2 196 | end 197 | 198 | function test_5(count) 199 | log.info('duplicating %d arrays', count) 200 | local rv = fun.duplicate{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}:take(count):totable() 201 | log.info('%s', json.encode(rv)) 202 | return rv 203 | end 204 | 205 | function test_6(...) 206 | return ... 207 | end 208 | 209 | iproto_connect_counter = 0 210 | function iproto_connect_count() 211 | return iproto_connect_counter 212 | end 213 | 214 | box.session.on_connect(function() 215 | -- box.session.type() was introduced in 1.7.4-370-g0bce2472b. 216 | -- 217 | -- We're interested in iproto sessions, but it is okay for our 218 | -- usage scenario to count replication and console sessions 219 | -- too: we only see to a delta and AFAIK our testing harness 220 | -- does not perform any background reconnections. 221 | if box.session.type == nil or box.session.type() == 'binary' then 222 | iproto_connect_counter = iproto_connect_counter + 1 223 | end 224 | end) 225 | 226 | -- export tarantool_version_at_least function 227 | _G.tarantool_version_at_least = tarantool_version_at_least 228 | 229 | require('console').listen(os.getenv('ADMIN_PORT')) 230 | 231 | -------------------------------------------------------------------------------- /test/shared/phpunit.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | test/AssertTest.php 10 | test/CreateTest.php 11 | test/DMLTest.php 12 | test/MockTest.php 13 | test/MsgPackTest.php 14 | test/RandomTest.php 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/shared/tarantool-1.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | extension = ./tarantool.so 3 | 4 | [tarantool] 5 | tarantool.timeout = 10 6 | tarantool.request_timeout = 0.1 7 | tarantool.con_per_host = 5 8 | tarantool.persistent = 0 9 | tarantool.retry_count = 10 10 | 11 | [xdebug] 12 | remote_autostart = 0 13 | remote_enabled = 0 14 | profiler_enabled = 0 15 | 16 | [zend] 17 | enable_gc = 0 18 | -------------------------------------------------------------------------------- /test/shared/tarantool-2.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | extension = ./tarantool.so 3 | 4 | [tarantool] 5 | tarantool.timeout = 10 6 | tarantool.request_timeout = 0.1 7 | tarantool.con_per_host = 5 8 | tarantool.persistent = 1 9 | tarantool.retry_count = 10 10 | 11 | [xdebug] 12 | remote_autostart = 0 13 | remote_enabled = 0 14 | profiler_enabled = 0 15 | 16 | [zend] 17 | enable_gc = 0 18 | -------------------------------------------------------------------------------- /test/shared/tarantool-3.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | extension = ./tarantool.so 3 | 4 | [tarantool] 5 | tarantool.timeout = 10 6 | tarantool.request_timeout = 10 7 | tarantool.con_per_host = 1 8 | tarantool.persistent = 1 9 | tarantool.retry_count = 2 10 | 11 | [xdebug] 12 | remote_autostart = 0 13 | remote_enabled = 0 14 | profiler_enabled = 0 15 | 16 | [zend] 17 | enable_gc = 0 18 | -------------------------------------------------------------------------------- /test/shared/valgrind.sup: -------------------------------------------------------------------------------- 1 | { 2 | 3 | Memcheck:Addr1 4 | ... 5 | fun:tarantool_free 6 | ... 7 | } 8 | { 9 | 10 | Memcheck:Addr4 11 | ... 12 | fun:tarantool_free 13 | ... 14 | } 15 | { 16 | 17 | Memcheck:Leak 18 | ... 19 | fun:ZEND_SEND_VAL_SPEC_CONST_HANDLER 20 | ... 21 | } 22 | { 23 | 24 | Memchek:Leak 25 | ... 26 | fun:debug_backtrace_get_args.isra.9 27 | ... 28 | } 29 | --------------------------------------------------------------------------------