[
  {
    "path": ".gitattributes",
    "content": "*.t linguist-language=Text\n"
  },
  {
    "path": ".github/workflows/coverity.yml",
    "content": "name: Coverity\n\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\njobs:\n  scan:\n    runs-on: ubuntu-18.04\n    if: ${{ github.repository_owner == 'openresty' }}\n    env:\n      COVERITY_SCAN_PROJECT_NAME: 'drizzle-nginx-module'\n      COVERITY_SCAN_BRANCH_PATTERN: '*'\n      COVERITY_SCAN_NOTIFICATION_EMAIL: 'chipitsine@gmail.com'\n      LUAJIT_PREFIX: '/opt/luajit21'\n      LUAJIT_LIB: '/opt/luajit21/lib'\n      LUAJIT_INC: '/opt/luajit21/include/luajit-2.1'\n      LUA_INCLUDE_DIR: '/opt/luajit21/include/luajit-2.1'\n      LUA_CMODULE_DIR: '/lib'\n      JOBS: 3\n      NGX_BUILD_JOBS: 3\n      NGINX_VERSION: 1.19.9\n      CC: gcc\n    steps:\n    - uses: actions/checkout@v3\n    - name: Install apt dependencies\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y axel libgd-dev\n    - name: clone OpenResty satellites\n      run: |\n        git clone https://github.com/openresty/nginx-devel-utils.git\n        git clone https://github.com/openresty/openresty.git ../openresty\n        git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx\n        git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module\n        git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module\n        git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core\n        git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache\n        git clone https://github.com/openresty/nginx-eval-module.git ../eval-nginx-module\n        git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module\n        git clone https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module\n        git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module\n        git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module\n        git clone https://github.com/openresty/openresty.git ../ngx_openresty\n    - name: Install libdrizzle\n      run: |\n        wget http://openresty.org/download/drizzle7-2011.07.21.tar.gz\n        tar xzf drizzle7-2011.07.21.tar.gz && cd drizzle7-2011.07.21\n        ./configure --prefix=/usr --without-server\n        sudo PATH=$PATH make libdrizzle-1.0 install-libdrizzle-1.0\n    - name: Install luajit2\n      run: |\n        git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git\n        cd luajit2\n        make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT'\n        sudo make install PREFIX=$LUAJIT_PREFIX\n    - name: Run Coverity Scan\n      env:\n        COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}\n      run: |\n        export COVERITY_SCAN_BUILD_COMMAND=\"sh util/build.sh $NGINX_VERSION\"\n        export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH\n        export NGX_BUILD_CC=gcc\n        curl -fsSL \"https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh\" | bash || true\n"
  },
  {
    "path": ".gitignore",
    "content": "*.mobi\ngenmobi.sh\n.libs\n*.swp\n*.slo\n*.la\n*.swo\n*.lo\n*~\n*.o\nprint.txt\n.rsync\n*.tar.gz\ndist\nbuild[789]\nbuild\ntags\nupdate-readme\n*.tmp\ntest/Makefile\ntest/blib\ntest.sh\nt.sh\nt/t.sh\ntest/t/servroot/\nreleng\nreset\n*.t_\nsrc/handler.h\nsrc/util.c\nsrc/module.h\nsrc/module.c\nsrc/drizzle.c\nsrc/processor.h\nsrc/handler.c\nsrc/util.h\nsrc/drizzle.h\nsrc/processor.c\nsrc/output.c\nsrc/output.h\nlibdrizzle\nctags\nsrc/stream.h\nnginx\nkeepalive\nreindex\nsrc/keepalive.c\nsrc/keepalive.h\nsrc/checker.h\nsrc/checker.c\nsrc/quoting.h\nsrc/quoting.c\nsrc/upstream.[ch]\nall\ngo\nbuildroot/\npack\nt/servroot/\nbuild1[0-9]\n*.plist\nMakefile\n*.patch\nanalyze\n*.html\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\ndist: focal \n\nbranches:\n  only:\n    - \"master\"\n\nos: linux\n\nlanguage: c\n\ncache:\n  apt: true\n  directories:\n  - download-cache\n\naddons:\n  apt:\n    packages:\n    - axel\n    - cpanminus\n    - libtest-base-perl\n    - libtext-diff-perl\n    - liburi-perl\n    - libwww-perl\n    - libtest-longstring-perl\n    - liblist-moreutils-perl\n    - libgd-dev\n\nservices:\n - mysql\n\ncompiler:\n  - gcc\n\nenv:\n  global:\n    - LUAJIT_PREFIX=/opt/luajit21\n    - LUAJIT_LIB=$LUAJIT_PREFIX/lib\n    - LIBDRIZZLE_PREFIX=/opt/drizzle\n    - LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0\n    - LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib\n    - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1\n    - LUA_INCLUDE_DIR=$LUAJIT_INC\n    - LUA_CMODULE_DIR=/lib\n    - JOBS=3\n    - NGX_BUILD_JOBS=$JOBS\n    - TEST_NGINX_SLEEP=0.006\n  matrix:\n    - NGINX_VERSION=1.29.8\n\ninstall:\n  - if [ ! -f download-cache/drizzle7-2011.07.21.tar.gz ]; then wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-2011.07.21.tar.gz; fi\n  - git clone https://github.com/openresty/nginx-devel-utils.git\n  - git clone https://github.com/openresty/openresty.git ../openresty\n  - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx\n  - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module\n  - git clone https://github.com/openresty/test-nginx.git\n  - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git\n  - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module\n  - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core\n  - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache\n  - git clone https://github.com/openresty/nginx-eval-module.git ../eval-nginx-module\n  - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module\n  - git clone https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module\n  - git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module\n  - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module\n  - git clone https://github.com/openresty/openresty.git ../ngx_openresty\n\nbefore_script:\n  - mysql -uroot -e \"create database ngx_test; CREATE USER 'ngx_test'@'%' IDENTIFIED WITH mysql_native_password BY 'ngx_test'; grant all on ngx_test.* to 'ngx_test'@'%'; flush privileges;\"\n\nscript:\n  - tar xzf download-cache/drizzle7-2011.07.21.tar.gz && cd drizzle7-2011.07.21\n  - ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server > build.log 2>&1 || (cat build.log && exit 1)\n  - sudo PATH=$PATH make libdrizzle-1.0 install-libdrizzle-1.0 > build.log 2>&1 || (cat build.log && exit 1)\n  - cd ../luajit2\n  - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)\n  - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)\n  - cpanm --sudo --notest Test::Nginx IPC::Run Test2::Util > build.log 2>&1 || (cat build.log && exit 1)\n  - cd ..\n  - cd test-nginx && sudo cpanm . && cd ..\n  - export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH\n  - export NGX_BUILD_CC=$CC\n  - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)\n  - nginx -V\n  - ldd `which nginx`|grep luajit\n  - prove -I. -r t\n"
  },
  {
    "path": "Changes",
    "content": "v0.1.0 - 5 July 2011\n* now we require at least libdrizzle 1.0, which supports official mysql 5.5+ and is much more stable under load. thanks Taylor Weibley for pushing this. We no longer require patching libdrizzle any more.\n\n* fixed a compilation issue on Mac OS X: we should include drizzle.h prior to nginx headers, or we will not get the \"bool\" type properly installed on Mac OS X.\n\n* fixed the spots that trigger -Wunused-but-set-variable by gcc 4.6.\n\n* fixed the duplicate last chunk issue: we should not set the last_buf flag ourselves in drizzle_output.c because ngx_http_upstream already sends a last buf for us.\n\n* ported over Maxim Dounin's patch for ngx_http_upstream_keepalive connection pool fixes: we should have discarded stale read events for cached tcp connections in the pool.\n\n* implemented the new drizzle_status directive to provide connection pool status monitoring capability.\n\n* fixed a minor bug in the connection pool: we should resume the \"name\", \"sockaddr\", and \"socklen\" fields for the connection from the pool such that we can get more detailed error log messages with the \"upstream: drizzle://ip.add.re.ss:port\" bit.\n\n* implemented the $drizzle_thread_id variable which is automatically set when mysql/drizzle times out.\n\n* now we use the 2-clause bsd license.\n\n* report an error message when upstream name not found for drizzle_pass.\n\n* now we implemented the charset option for the \"drizzle_server\" diredctive which causes ngx_drizzle send \"set names xxx\" automatcially for every connection to that drizzle server.\n\n* added a lot of more documentation.\n\n"
  },
  {
    "path": "README.markdown",
    "content": "Name\n====\n\ndrizzle-nginx-module - Upstream module for talking to MySQL and Drizzle directly\n\n*This module is not distributed with the Nginx source.* See [the installation instructions](#installation).\n\nTable of Contents\n=================\n\n* [Status](#status)\n* [Version](#version)\n* [Synopsis](#synopsis)\n* [Description](#description)\n    * [Keepalive connection pool](#keepalive-connection-pool)\n    * [Last Insert ID](#last-insert-id)\n* [Directives](#directives)\n    * [drizzle_server](#drizzle_server)\n    * [drizzle_keepalive](#drizzle_keepalive)\n    * [drizzle_query](#drizzle_query)\n    * [drizzle_pass](#drizzle_pass)\n    * [drizzle_connect_timeout](#drizzle_connect_timeout)\n    * [drizzle_send_query_timeout](#drizzle_send_query_timeout)\n    * [drizzle_recv_cols_timeout](#drizzle_recv_cols_timeout)\n    * [drizzle_recv_rows_timeout](#drizzle_recv_rows_timeout)\n    * [drizzle_buffer_size](#drizzle_buffer_size)\n    * [drizzle_module_header](#drizzle_module_header)\n    * [drizzle_status](#drizzle_status)\n* [Variables](#variables)\n    * [$drizzle_thread_id](#drizzle_thread_id)\n* [Output Format](#output-format)\n    * [RDS Header Part](#rds-header-part)\n    * [RDS Body Part](#rds-body-part)\n        * [Columns](#columns)\n        * [Rows](#rows)\n            * [Row Flag](#row-flag)\n            * [Fields Data](#fields-data)\n    * [RDS buffer Limitations](#rds-buffer-limitations)\n* [Status Code](#status-code)\n* [Caveats](#caveats)\n* [Trouble Shooting](#trouble-shooting)\n* [Known Issues](#known-issues)\n* [Installation](#installation)\n* [Compatibility](#compatibility)\n* [Community](#community)\n    * [English Mailing List](#english-mailing-list)\n    * [Chinese Mailing List](#chinese-mailing-list)\n* [Report Bugs](#report-bugs)\n* [Source Repository](#source-repository)\n* [Test Suite](#test-suite)\n* [TODO](#todo)\n* [Changes](#changes)\n* [Authors](#authors)\n* [Copyright & License](#copyright--license)\n* [See Also](#see-also)\n\nStatus\n======\n\nThis module is already production ready.\n\nVersion\n=======\n\nThis document describes ngx_drizzle [v0.1.11](https://github.com/openresty/drizzle-nginx-module/tags) released on 19 April 2018.\n\nSynopsis\n========\n\n```nginx\n\n http {\n     ...\n\n     upstream cluster {\n         # simple round-robin\n         drizzle_server 127.0.0.1:3306 dbname=test\n              password=some_pass user=monty protocol=mysql;\n         drizzle_server 127.0.0.1:1234 dbname=test2\n              password=pass user=bob protocol=drizzle;\n     }\n\n     upstream backend {\n         drizzle_server 127.0.0.1:3306 dbname=test\n              password=some_pass user=monty protocol=mysql;\n     }\n\n     server {\n         location /mysql {\n             set $my_sql 'select * from cats';\n             drizzle_query $my_sql;\n\n             drizzle_pass backend;\n\n             drizzle_connect_timeout    500ms; # default 60s\n             drizzle_send_query_timeout 2s;    # default 60s\n             drizzle_recv_cols_timeout  1s;    # default 60s\n             drizzle_recv_rows_timeout  1s;    # default 60s\n         }\n\n         ...\n\n         # for connection pool monitoring\n         location /mysql-pool-status {\n             allow 127.0.0.1;\n             deny all;\n\n             drizzle_status;\n         }\n     }\n }\n```\n\n[Back to TOC](#table-of-contents)\n\nDescription\n===========\n\nThis is an nginx upstream module integrating [libdrizzle](https://launchpad.net/drizzle) into Nginx in a non-blocking and streamming way.\n\nEssentially it provides a very efficient and flexible way for nginx internals to access MySQL, Drizzle, as well as other RDBMS's that support the Drizzle or MySQL wired protocol. Also it can serve as a direct REST interface to those RDBMS backends.\n\nThis module does not generate human-readable outputs, rather, in a binary format called Resty-DBD-Stream (RDS) designed by ourselves. You usually need other components, like [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module), [rds-csv-nginx-module](http://github.com/openresty/rds-csv-nginx-module), or [lua-rds-parser](http://github.com/openresty/lua-rds-parser), to work with this module. See [Output Format](#output-format) for details.\n\n[Back to TOC](#table-of-contents)\n\nKeepalive connection pool\n-------------------------\n\nThis module also provides a builtin per-worker connection pool mechanism for MySQL or Drizzle TCP connections.\n\nHere's a sample configuration:\n\n```nginx\n\n upstream backend {\n     drizzle_server 127.0.0.1:3306 dbname=test\n          password=some_pass user=monty protocol=mysql;\n     drizzle_keepalive max=100 mode=single overflow=reject;\n }\n```\n\nFor now, the connection pool uses a simple LIFO algorithm to assign idle connections in the pool. That is, most recently (successfully) used connections will be reused first the next time. And new idle connections will always replace the oldest idle connections in the pool even if the pool is already full.\n\nSee the [drizzle_keepalive](#drizzle_keepalive) directive for more details.\n\n[Back to TOC](#table-of-contents)\n\nLast Insert ID\n--------------\nIf you want to get LAST_INSERT_ID, then ngx_drizzle already returns that automatically for you when you're doing a SQL insert query. Consider the following sample `nginx.conf` snippet:\n```nginx\n\n location /test {\n     echo_location /mysql \"drop table if exists foo\";\n     echo;\n     echo_location /mysql \"create table foo (id serial not null, primary key (id), val real);\";\n     echo;\n     echo_location /mysql \"insert into foo (val) values (3.1415926);\";\n     echo;\n     echo_location /mysql \"select * from foo;\";\n     echo;\n }\n\n location /mysql {\n     drizzle_pass backend;\n     drizzle_module_header off;\n     drizzle_query $query_string;\n     rds_json on;\n }\n```\nThen request `GET /test` gives the following outputs:\n```javascript\n\n {\"errcode\":0}\n {\"errcode\":0}\n {\"errcode\":0,\"insert_id\":1,\"affected_rows\":1}\n [{\"id\":1,\"val\":3.1415926}]\n```\nYou can see the `insert_id` field (as well as the `affected_rows` field in the 3rd JSON response.\n\n[Back to TOC](#table-of-contents)\n\nDirectives\n==========\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_server\n--------------\n**syntax:** *drizzle_server &lt;host&gt; user=&lt;user&gt; password=&lt;pass&gt; dbname=&lt;database&gt;*\n\n**syntax:** *drizzle_server &lt;host&gt;:&lt;port&gt; user=&lt;user&gt; password=&lt;pass&gt; dbname=&lt;database&gt; protocol=&lt;protocol&gt; charset=&lt;charset&gt;*\n\n**default:** *no*\n\n**context:** *upstream*\n\nDirective assigns the name and the parameters of server. For the name it is possible to use a domain name, an address, with an optional port (default: 3306). If domain name resolves to several addresses, then all are used.\n\nThe following options are supported:\n\n**user=**`<user>`\n\tMySQL/Drizzle user name `<user>` for login.\n\n**password=**`<pass>`\n\tSpecify mysql password `<pass>`for login. If you have special characters like `#` or spaces in your password text, then you'll have to quote the whole key-value pair with either single-quotes or double-quotes, as in\n\n```nginx\n\n drizzle_server 127.0.0.1:3306 user=monty \"password=a b#1\"\n         dbname=test protocol=mysql;\n```\n\n**dbname=**`<database>`\n\tSpecify default MySQL database `<database>` for the connection. Note that MySQL does allow referencing tables belonging to different databases by qualifying table names with database names in SQL queries.\n\n**protocol=**`<protocol>`\n\tSpecify which wire protocol to use, `drizzle` or `mysql`. Default to `drizzle`.\n\n**charset=**`<charset>`\n\tExplicitly specify the character set for the MySQL connections. Setting this option to a non-empty value will make this module send out a `set names '<charset>'` query right after the mysql connection is established.\n\tIf the default character encoding of the MySQL connection is already what you want, you needn't set this option because it has extra runtime cost.\n\tHere is a small example:\n```nginx\n\n drizzle_server foo.bar.com:3306 user=monty password=some_pass\n                                 dbname=test protocol=mysql\n                                 charset=utf8;\n```\nPlease note that for the mysql server, \"utf-8\" is not a valid encoding name while `utf8` is.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_keepalive\n-----------------\n**syntax:** *drizzle_keepalive max=&lt;size&gt; mode=&lt;mode&gt;*\n\n**default:** *drizzle_keepalive max=0 mode=single*\n\n**context:** *upstream*\n\nConfigures the keep-alive connection pool for MySQL/Drizzle connections.\n\nThe following options are supported:\n\n**max=**`<num>`\n\tSpecify the capacity of the connection pool for the current upstream block. The <num> value *must* be non-zero. If set to `0`, it effectively disables the connection pool. This option is default to `0`.\n\n**mode=**`<mode>`\n\tThis supports two values, `single` and `multi`. The `single` mode means the pool does not distinguish various drizzle servers in the current upstream block while `multi` means the pool will merely reuse connections which have identical server host names and ports. Note that even under `multi`, differences between `dbname` or `user` parameters will be silently ignored. Default to `single`.\n\n**overflow=**`<action>`\n\tThis option specifies what to do when the connection pool is already full while new database connection is required. Either `reject` or `ignore` can be specified. In case of `reject`, it will reject the current request, and returns the `503 Service Unavailable` error page. For `ignore`, this module will go on creating a new database connection.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_query\n-------------\n**syntax:** *drizzle_query &lt;sql&gt;*\n\n**default:** *no*\n\n**context:** *http, server, location, location if*\n\nSpecify the SQL queries sent to the Drizzle/MySQL backend.\n\nNginx variable interpolation is supported, but you must be careful with SQL injection attacks. You can use the [set_quote_sql_str](http://github.com/openresty/set-misc-nginx-module#set_quote_sql_str) directive, for example, to quote values for SQL interpolation:\n\n```nginx\n\n location /cat {\n     set_unescape_uri $name $arg_name;\n     set_quote_sql_str $quoted_name $name;\n\n     drizzle_query \"select * from cats where name = $quoted_name\";\n     drizzle_pass my_backend;\n }\n```\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_pass\n------------\n**syntax:** *drizzle_pass &lt;remote&gt;*\n\n**default:** *no*\n\n**context:** *location, location if*\n\n**phase:** *content*\n\nThis directive specifies the Drizzle or MySQL upstream name to be queried in the current location. The `<remote>` argument can be any upstream name defined with the [drizzle_server](#drizzle_server) directive.\n\nNginx variables can also be interpolated into the `<remote>` argument, so as to do dynamic backend routing, for example:\n```nginx\n\n upstream moon { drizzle_server ...; }\n\n server {\n     location /cat {\n         set $backend 'moon';\n\n         drizzle_query ...;\n         drizzle_pass $backend;\n     }\n }\n```\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_connect_timeout\n-----------------------\n**syntax:** *drizzle_connect_time &lt;time&gt;*\n\n**default:** *drizzle_connect_time 60s*\n\n**context:** *http, server, location, location if*\n\nSpecify the (total) timeout for connecting to a remote Drizzle or MySQL server.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, i.e., \"second\". The default setting is `60s`.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_send_query_timeout\n--------------------------\n**syntax:** *drizzle_send_query_timeout &lt;time&gt;*\n\n**default:** *drizzle_send_query_timeout 60s*\n\n**context:** *http, server, location, location if*\n\nSpecify the (total) timeout for sending a SQL query to a remote Drizzle or MySQL server.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, ie, \"second\". The default setting is `60s`.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_recv_cols_timeout\n-------------------------\n**syntax:** *drizzle_recv_cols_timeout &lt;time&gt;*\n\n**default:** *drizzle_recv_cols_timeout 60s*\n\n**context:** *http, server, location, location if*\n\nSpecify the (total) timeout for receiving the columns metadata of the result-set to a remote Drizzle or MySQL server.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, ie, \"second\". The default setting is `60s`.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_recv_rows_timeout\n-------------------------\n**syntax:** *drizzle_recv_rows_timeout &lt;time&gt;*\n\n**default:** *drizzle_recv_rows_timeout 60s*\n\n**context:** *http, server, location, location if*\n\nSpecify the (total) timeout for receiving the rows data of the result-set (if any) to a remote Drizzle or MySQL server.\n\nThe `<time>` argument can be an integer, with an optional time unit, like `s` (second), `ms` (millisecond), `m` (minute). The default time unit is `s`, ie, \"second\". The default setting is `60s`.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_buffer_size\n-------------------\n**syntax:** *drizzle_buffer_size &lt;size&gt;*\n\n**default:** *drizzle_buffer_size 4k/8k*\n\n**context:** *http, server, location, location if*\n\nSpecify the buffer size for drizzle outputs. Default to the page size (4k/8k). The larger the buffer, the less streammy the outputing process will be.\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_module_header\n---------------------\n**syntax:** *drizzle_module_header on|off*\n\n**default:** *drizzle_module_header on*\n\n**context:** *http, server, location, location if*\n\nControls whether to output the drizzle header in the response. Default on.\n\nThe drizzle module header looks like this:\n\n\n    X-Resty-DBD-Module: ngx_drizzle 0.1.0\n\n\n[Back to TOC](#table-of-contents)\n\ndrizzle_status\n--------------\n**syntax:** *drizzle_status*\n\n**default:** *no*\n\n**context:** *location, location if*\n\n**phase:** *content*\n\nWhen specified, the current Nginx location will output a status report for all the drizzle upstream servers in the virtual server of the current Nginx worker process.\n\nThe output looks like this:\n\n    worker process: 15231\n\n    upstream backend\n      active connections: 0\n      connection pool capacity: 10\n      overflow: reject\n      cached connection queue: 0\n      free'd connection queue: 10\n      cached connection successfully used count:\n      free'd connection successfully used count: 3 0 0 0 0 0 0 0 0 0\n      servers: 1\n      peers: 1\n\n    upstream backend2\n      active connections: 0\n      connection pool capacity: 0\n      servers: 1\n      peers: 1\n\nNote that, this is *not* the global statistics if you do have multiple Nginx worker processes configured in your `nginx.conf`.\n\n[Back to TOC](#table-of-contents)\n\nVariables\n=========\n\nThis module creates the following Nginx variables:\n\n[Back to TOC](#table-of-contents)\n\n$drizzle_thread_id\n------------------\n\nThis variable will be assigned a textual number of the underlying MySQL or Drizzle query thread ID when the current SQL query times out. This thread ID can be further used in a SQL kill command to cancel the timed-out query.\n\nHere's an example:\n```nginx\n\n drizzle_connect_timeout 1s;\n drizzle_send_query_timeout 2s;\n drizzle_recv_cols_timeout 1s;\n drizzle_recv_rows_timeout 1s;\n\n location /query {\n     drizzle_query 'select sleep(10)';\n     drizzle_pass my_backend;\n     rds_json on;\n\n     more_set_headers -s 504 'X-Mysql-Tid: $drizzle_thread_id';\n }\n\n location /kill {\n     drizzle_query \"kill query $arg_tid\";\n     drizzle_pass my_backend;\n     rds_json on;\n }\n\n location /main {\n     content_by_lua '\n         local res = ngx.location.capture(\"/query\")\n         if res.status ~= ngx.HTTP_OK then\n             local tid = res.header[\"X-Mysql-Tid\"]\n             if tid and tid ~= \"\" then\n                 ngx.location.capture(\"/kill\", { args = {tid = tid} })\n             end\n             return ngx.HTTP_INTERNAL_SERVER_ERROR;\n         end\n         ngx.print(res.body)\n     '\n }\n```\nwhere we make use of [headers-more-nginx-module](http://github.com/openresty/headers-more-nginx-module), [lua-nginx-module](http://github.com/openresty/lua-nginx-module), and [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module) too. When the SQL query timed out, we'll explicitly cancel it immediately. One pitfall here is that you have to add these modules in this order while building Nginx:\n\n* [lua-nginx-module](http://github.com/openresty/lua-nginx-module)\n* [headers-more-nginx-module](http://github.com/openresty/headers-more-nginx-module)\n* [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module)\n\nSuch that, their output filters will work in the *reversed* order, i.e., first convert RDS to JSON, and then add our `X-Mysql-Tid` custom header, and finally capture the whole (subrequest) response with the Lua module. You're recommended to use the [OpenResty bundle](http://openresty.org/) though, it ensures the module building order automatically for you.\n\n[Back to TOC](#table-of-contents)\n\nOutput Format\n=============\n\nThis module generates binary query results in a format that is shared among the various Nginx database driver modules like [ngx_postgres](http://github.com/FRiCKLE/ngx_postgres/). This data format is named `Resty DBD Stream` (RDS).\n\nIf you're a web app developer, you may be more interested in\n\n* using [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module) to obtain JSON output,\n* using [rds-csv-nginx-module](http://github.com/openresty/rds-csv-nginx-module) to obain Comma-Separated-Value (CSV) output,\n* or using [lua-rds-parser](http://github.com/openresty/lua-rds-parser) to parse the RDS data into Lua data structures.\n\nFor the HTTP response header part, the `200 OK` status code should always be returned. The `Content-Type` header *must* be set to `application/x-resty-dbd-stream`. And the driver generating this response also sets a `X-Resty-DBD` header. For instance, this module adds the following output header:\n\n    X-Resty-DBD-Module: drizzle 0.1.0\n\nwhere `0.1.0` is this module's own version number. This `X-Resty-DBD-Module` header is optional though.\n\nBelow is the HTTP response body format (version 0.0.3):\n\n[Back to TOC](#table-of-contents)\n\nRDS Header Part\n---------------\n\nThe RDS Header Part consists of the following fields:\n\n**uint8_t**\n\tendian type (1 means big-endian and little endian otherwise)\n\n**uint32_t**\n\tformat version (v1.2.3 is represented as 1002003 in decimal)\n\n**uint8_t**\n\tresult type (0 means normal SQL result type, fixed for now)\n\n**uint16_t**\n\tstandard error code\n\n**uint16_t**\n\tdriver-specific error code\n\n**uint16_t**\n\tdriver-specific error string length\n\n**u_char ***\n\tdriver-specific error string data\n\n**uint64_t**\n\tdatabase rows affected\n\n**uint64_t**\n\tinsert id (if none, 0)\n\n**uint16_t**\n\tcolumn count\n\n[Back to TOC](#table-of-contents)\n\nRDS Body Part\n-------------\n\nWhen the `column count` field in the [RDS Header Part](#rds-header-part) is zero, then the whole RDS Body Part is omitted.\n\nThe RDS Body Part consists of two sections, [Columns](#columns) and [Rows](#rows).\n\n### Columns\n\n\nThe columns part consists of zero or more column data. The number of columns is determined by `column count` field in [RDS Header Part](#rds-header-part).\n\nEach column consists of the following fields\n\n**uint16_t**\n\tnon-zero value for standard column type code and for the column list terminator and zero otherwise.\n\n**uint16_t**\n\tdriver-specific column type code\n\n**uint16_t**\n\tcolumn name length\n\n**u_char ***\n\tcolumn name data\n\n### Rows\n\n\nThe rows part consists of zero or more row data, terminated by a 8-bit zero.\n\nEach row data consists of a [Row Flag](#row-flag) and an optional [Fields Data](#fields-data) part.\n\n#### Row Flag\n\n\n**uint8_t**\n\tvalid row (1 means valid, and 0 means the row list terminator)\n\n#### Fields Data\n\n\nThe Fields Data consists zero or more fields of data. The field count is predetermined by the <code>column number</code) specified in [RDS Header Part](#rds-header-part).\n\n**uint32_t**\n\tfield length ((uint32_t) -1 represents NULL)\n\n**u_char ***\n\tfield data in textual representation), is empty (0) if field length == (uint32_t) -1\n\n[Back to TOC](#table-of-contents)\n\nRDS buffer Limitations\n----------------------\n\nOn the nginx output chain link level, the following components should be put into a single `ngx_buf_t` struct:\n\n* the header\n\n* each column and the column list terminator\n\n* each row's valid flag byte and row list terminator\n\n* each field in each row (if any) but the field data can span multiple bufs.\n\n[Back to TOC](#table-of-contents)\n\nStatus Code\n===========\n\nIf the MySQL error code in MySQL's query result is not OK, then a 500 error page is returned by this module, except for the table non-existent error, which results in the `410 Gone` error page.\n\n[Back to TOC](#table-of-contents)\n\nCaveats\n=======\n\n* Other usptream modules like `upstream_hash` and [HttpUpstreamKeepaliveModule](http://wiki.nginx.org/HttpUpstreamKeepaliveModule) *must not* be used with this module in a single upstream block.\n* Directives like [server](http://nginx.org/en/docs/http/ngx_http_upstream_module.html#server) *must not* be mixed with [drizzle_server](#drizzle_server) either.\n* Upstream backends that don't use [drizzle_server](#drizzle_server) to define server entries *must not* be used in the [drizzle_pass](#drizzle_pass) directive.\n\n[Back to TOC](#table-of-contents)\n\nTrouble Shooting\n================\n* When you see the following error message in `error.log`:\n\n        failed to connect: 15: drizzle_state_handshake_result_read:\n          old insecure authentication mechanism not supported in upstream, ...\n\n\tthen you may checkout if your MySQL is too old (at least 5.x is required) or your mysql config file explicitly forces the use of old authentication method (you should remove the `old-passwords` line from your `my.cnf` and add the line `secure_auth 1`).\n* When you see the following error message in `error.log`:\n\n        failed to connect: 23: Access denied for user 'root'@'ubuntu'\n          (using password: YES) while connecting to drizzle upstream, ...\n\n\tYou should check if your MySQL account does have got TCP login access on your MySQL server side. A quick check is to use MySQL's official client to connect to your server:\n```bash\n\n     mysql --protocol=tcp -u user --password=password -h foo.bar.com dbname\n```\n\tNote that the `--protocol=tcp` option is required here, or your MySQL client may use Unix Domain Socket to connect to your MySQL server.\n\n[Back to TOC](#table-of-contents)\n\nKnown Issues\n============\n\n* Calling mysql procedures are currently not supported because the underlying libdrizzle library does not support the `CLIENT_MULTI_RESULTS` flag yet :( But we'll surely work on it.\n* Multiple SQL statements in a single query are not supported due to the lack of `CLIENT_MULTI_STATEMENTS` support in the underlying libdrizzle library.\n* This module does not (yet) work with the `RTSIG` event model.\n\n[Back to TOC](#table-of-contents)\n\nInstallation\n============\nYou're recommended to install this module as well as [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module) via the OpenResty bundle:\n\n<http://openresty.org>\n\nThe installation steps are usually as simple as `./configure --with-http_drizzle_module && make && make install` (But you still need to install the libdrizzle library manually, see [<http://openresty.org/en/drizzle-nginx-module.html]>(http://openresty.org/en/drizzle-nginx-module.html) for detailed instructions.\n\nAlternatively, you can compile this module with Nginx core's source by hand:\n\n* You should first install libdrizzle 1.0 which is now distributed with the drizzle project and can be obtained from [<https://launchpad.net/drizzle]>(https://launchpad.net/drizzle). The latest drizzle7 release does not support building libdrizzle 1.0 separately and requires a lot of external dependencies like Boost and Protobuf which are painful to install. The last version supporting building libdrizzle 1.0 separately is `2011.07.21`. You can download it from <http://agentzh.org/misc/nginx/drizzle7-2011.07.21.tar.gz> . Which this version of drizzle7, installation of libdrizzle 1.0 is usually as simple as\n```nginx\n\n     tar xzvf drizzle7-2011.07.21.tar.gz\n     cd drizzle7-2011.07.21/\n     ./configure --without-server\n     make libdrizzle-1.0\n     make install-libdrizzle-1.0\n```\n\tEnsure that you have the `python` command point to a `python2` interpreter. It's known that on recent : Arch Linux distribution, `python` is linked to `python3` by default, and while running `make libdrizzle-1.0` will yield the error\n```bash\n\n     File \"config/pandora-plugin\", line 185\n         print \"Dependency loop detected with %s\" % plugin['name']\n                                                  ^\n     SyntaxError: invalid syntax\n     make: *** [.plugin.scan] Error 1\n```\n\tYou can fix this by pointing `python` to `python2`.\n* Download the latest version of the release tarball of this module from drizzle-nginx-module [file list](http://github.com/openresty/drizzle-nginx-module/tags).\n* Grab the nginx source code from [nginx.org](http://nginx.org/), for example, the version 1.13.6 (see [nginx compatibility](#compatibility)), and then build the source with this module:\n```bash\n\n     wget 'http://nginx.org/download/nginx-1.13.6.tar.gz'\n     tar -xzvf nginx-1.13.6.tar.gz\n     cd nginx-1.13.6/\n\n     # if you have installed libdrizzle to the prefix /opt/drizzle, then\n     # specify the following environments:\n     # export LIBDRIZZLE_INC=/opt/drizzle/include/libdrizzle-1.0\n     # export LIBDRIZZLE_LIB=/opt/drizzle/lib\n\n     # Here we assume you would install you nginx under /opt/nginx/.\n     ./configure --prefix=/opt/nginx \\\n                 --add-module=/path/to/drizzle-nginx-module\n\n     make -j2\n     make install\n```\n\nYou usually also need [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module) to obtain JSON output from the binary RDS output generated by this upstream module.\n\n[Back to TOC](#table-of-contents)\n\nCompatibility\n=============\n\nIf you're using MySQL, then MySQL `5.0 ~ 5.5` is required. We're not sure if MySQL `5.6+` work; reports welcome!\n\nThis module has been tested on Linux and Mac OS X. Reports on other POSIX-compliant systems will be highly appreciated.\n\nThe following versions of Nginx should work with this module:\n\n* 1.16.x\n* 1.15.x    (last tested: 1.15.8)\n* 1.14.x\n* 1.13.x    (last tested: 1.13.6)\n* 1.12.x\n* 1.11.x    (last tested: 1.11.2)\n* 1.10.x\n* 1.9.x     (last tested: 1.9.15)\n* 1.8.x\n* 1.7.x     (last tested: 1.7.10)\n* 1.6.x\n* 1.5.x     (last tested: 1.5.8)\n* 1.4.x     (last tested: 1.4.4)\n* 1.3.x     (last tested: 1.3.7)\n* 1.2.x     (last tested: 1.2.9)\n* 1.1.x     (last tested: 1.1.5)\n* 1.0.x     (last tested: 1.0.8)\n* 0.8.x     (last tested: 0.8.55)\n* 0.7.x >= 0.7.44 (last tested version is 0.7.67)\n\nEarlier versions of Nginx like `0.6.x` and `0.5.x` will *not* work.\n\nIf you find that any particular version of Nginx above `0.7.44` does not work with this module, please consider reporting a bug.\n\n[Back to TOC](#table-of-contents)\n\nCommunity\n=========\n\n[Back to TOC](#table-of-contents)\n\nEnglish Mailing List\n--------------------\n\nThe [openresty-en](https://groups.google.com/group/openresty-en) mailing list is for English speakers.\n\n[Back to TOC](#table-of-contents)\n\nChinese Mailing List\n--------------------\n\nThe [openresty](https://groups.google.com/group/openresty) mailing list is for Chinese speakers.\n\n[Back to TOC](#table-of-contents)\n\nReport Bugs\n===========\n\nPlease submit bug reports, wishlists, or patches by\n\n1. creating a ticket on the [issue tracking interface](http://github.com/openresty/drizzle-nginx-module/issues) provided by GitHub,\n1. or sending an email to the [OpenResty community](#community).\n\n[Back to TOC](#table-of-contents)\n\nSource Repository\n=================\n\nAvailable on github at [openresty/drizzle-nginx-module](http://github.com/openresty/drizzle-nginx-module).\n\n[Back to TOC](#table-of-contents)\n\nTest Suite\n==========\n\nThis module comes with a Perl-driven test suite. The [test cases](http://github.com/openresty/drizzle-nginx-module/tree/master/t/) are\n[declarative](http://github.com/openresty/drizzle-nginx-module/blob/master/t/sanity.t) too. Thanks to the [Test::Nginx](http://search.cpan.org/perldoc?Test::Nginx) module in the Perl world.\n\nTo run it on your side:\n\n```bash\n\n $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t\n```\n\nBecause a single nginx server (by default, `localhost:1984`) is used across all the test scripts (`.t` files), it's meaningless to run the test suite in parallel by specifying `-jN` when invoking the `prove` utility.\n\n[Back to TOC](#table-of-contents)\n\nTODO\n====\n* add the MySQL transaction support.\n* add multi-statement MySQL query support.\n* implement the \"drizzle_max_output_size\" directive. When the RDS data is larger then the size specified, the module will try to terminate the output as quickly as possible but will still ensure the resulting response body is still in valid RDS format.\n* implement the `drizzle_upstream_next` mechanism for failover support.\n* add support for multiple \"drizzle_query\" directives in a single location.\n* implement *weighted* round-robin algorithm for the upstream server clusters.\n* add the `max_idle_time` option to the [drizzle_server](#drizzle_server) directive, so that the connection pool will automatically release idle connections for the timeout you specify.\n* add the `min` option to the \"drizzle_server\" directive so that the connection pool will automatically create that number of connections and put them into the pool.\n* add Unix domain socket support in the `drizzle_server` directive.\n* make the [drizzle_query](#drizzle_query) directive reject variables that have not been processed by a [drizzle_process](#drizzle_process) directive. This will pretect us from SQL injections. There will also be an option (\"strict=no\") to disable such checks.\n\n[Back to TOC](#table-of-contents)\n\nChanges\n=======\n\nThe changes of every release of this module can be obtained from the OpenResty bundle's change logs:\n\n<http://openresty.org/#Changes>\n\n[Back to TOC](#table-of-contents)\n\nAuthors\n=======\n\n* chaoslawful (王晓哲) <chaoslawful at gmail dot com>\n* Yichun \"agentzh\" Zhang (章亦春) <agentzh at gmail dot com>, OpenResty Inc.\n* Piotr Sikora <piotr.sikora at frickle dot com>, Google Inc.\n\n[Back to TOC](#table-of-contents)\n\nCopyright & License\n===================\n\nThis module is licenced under the BSD license.\n\nCopyright (C) 2009-2014, by Xiaozhe Wang (chaoslawful) <chaoslawful@gmail.com>.\n\nCopyright (C) 2009-2018, by Yichun \"agentzh\" Zhang (章亦春) <agentzh@gmail.com>, OpenResty Inc.\n\nCopyright (C) 2010-2014, by FRiCKLE Piotr Sikora <info@frickle.com>, Google Inc.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n[Back to TOC](#table-of-contents)\n\nSee Also\n========\n\n* [rds-json-nginx-module](http://github.com/openresty/rds-json-nginx-module)\n* [rds-csv-nginx-module](http://github.com/openresty/rds-csv-nginx-module)\n* [lua-rds-parser](http://github.com/openresty/lua-rds-parser)\n* [The OpenResty bundle](http://openresty.org)\n* [DrizzleNginxModule bundled by OpenResty](http://openresty.org/#DrizzleNginxModule)\n* [postgres-nginx-module](http://github.com/FRiCKLE/ngx_postgres)\n* [lua-nginx-module](http://github.com/openresty/lua-nginx-module)\n* The [lua-resty-mysql](https://github.com/openresty/lua-resty-mysql) library based on the [lua-nginx-module](http://github.com/openresty/lua-nginx-module) cosocket API.\n\n"
  },
  {
    "path": "config",
    "content": "ngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <libdrizzle/drizzle_client.h>\"\nngx_feature_test=\"drizzle_version();\"\n\nif [ -n \"$LIBDRIZZLE_INC\" -o -n \"$LIBDRIZZLE_LIB\" ]; then\n    # explicit set libdrizzle lib path\n    ngx_feature=\"libdrizzle library in directories specified by LIBDRIZZLE_INC ($LIBDRIZZLE_INC) and LIBDRIZZLE_LIB ($LIBDRIZZLE_LIB)\"\n    ngx_feature_path=\"$LIBDRIZZLE_INC\"\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R$LIBDRIZZLE_LIB -L$LIBDRIZZLE_LIB -ldrizzle\"\n    else\n        ngx_feature_libs=\"-L$LIBDRIZZLE_LIB -ldrizzle\"\n    fi\n    . auto/feature\nelse\n    # auto-discovery\n    ngx_feature=\"libdrizzle library\"\n    ngx_feature_path=\n    ngx_feature_libs=\"-ldrizzle\"\n    . auto/feature\n\n    if [ $ngx_found = no ]; then\n        # Ubuntu 12.04\n        ngx_feature=\"libdrizzle library in /usr/local/\"\n        ngx_feature_path=\"/usr/include/libdrizzle-1.0\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/lib -L/usr/lib -ldrizzle\"\n        else\n            ngx_feature_libs=\"-L/usr/lib -ldrizzle\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # FreeBSD, OpenBSD\n        ngx_feature=\"libdrizzle library in /usr/local/\"\n        ngx_feature_path=\"/usr/local/include/libdrizzle-1.0\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -ldrizzle\"\n        else\n            ngx_feature_libs=\"-L/usr/local/lib -ldrizzle\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # NetBSD\n        ngx_feature=\"libdrizzle library in /usr/pkg/\"\n        ngx_feature_path=\"/usr/pkg/include/libdrizzle-1.0\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -ldrizzle\"\n        else\n            ngx_feature_libs=\"-L/usr/pkg/lib -ldrizzle\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # MacPorts\n        ngx_feature=\"libdrizzle library in /opt/local/\"\n        ngx_feature_path=\"/opt/local/include/libdrizzle-1.0\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -ldrizzle\"\n        else\n            ngx_feature_libs=\"-L/opt/local/lib -ldrizzle\"\n        fi\n        . auto/feature\n    fi\nfi\n\nif [ $ngx_found = yes ]; then\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n    CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\nelse\n cat << END\n $0: error: the ngx_drizzle addon requires the libdrizzle library.\nEND\n exit 1\nfi\n\nngx_addon_name=ngx_http_drizzle_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_drizzle_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_drizzle_module.c $ngx_addon_dir/src/ngx_http_drizzle_handler.c $ngx_addon_dir/src/ngx_http_drizzle_processor.c $ngx_addon_dir/src/ngx_http_drizzle_upstream.c $ngx_addon_dir/src/ngx_http_drizzle_util.c $ngx_addon_dir/src/ngx_http_drizzle_output.c $ngx_addon_dir/src/ngx_http_drizzle_keepalive.c $ngx_addon_dir/src/ngx_http_drizzle_quoting.c $ngx_addon_dir/src/ngx_http_drizzle_checker.c\"\nNGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_drizzle_module.h $ngx_addon_dir/src/ngx_http_drizzle_handler.h $ngx_addon_dir/src/ngx_http_drizzle_processor.h $ngx_addon_dir/src/ngx_http_drizzle_upstream.h $ngx_addon_dir/src/ngx_http_drizzle_util.h $ngx_addon_dir/src/ngx_http_drizzle_output.h $ngx_addon_dir/src/resty_dbd_stream.h $ngx_addon_dir/src/ngx_http_drizzle_keepalive.h $ngx_addon_dir/src/ngx_http_drizzle_quoting.h $ngx_addon_dir/src/ngx_http_drizzle_checker.h\"\n\nhave=NGX_DRIZZLE_MODULE . auto/have\n"
  },
  {
    "path": "doc/design",
    "content": "main request to location \"/drizzle\" (handled by mod_drizzle)\n\t-> mod_drizzle content handler (is main request)\n\t\t-> emit subrequest to location \"/drizzle\"\n\t\t-> return NGX_DONE\n\t-> mod_drizzle content handler (is subrequest)\n\t\t-> connect db (through libdrizzle), get db connection fd\n\t\t-> wrap db connection fd into a ngx_event, reg rd/wr event handler\n\t\t-> return NGX_DONE\n\t-> i/o event occured on db connection\n\t\t-> rd/wr event handler called, call mod_drizzle content handler using subrequest struct\n\t\t\t-> mod_drizzle content handler called (is subrequest)\n\t\t\t\t-> continue libdrizzle state machine\n\t\t\t\t-> generate response header and body if done,\n\t\t\t\t\tthen unreg db connection events,\n\t\t\t\t\tand call ngx_http_finalize_request to release self\n\t\t\t\t-> return NGX_OK\n"
  },
  {
    "path": "src/ddebug.h",
    "content": "#ifndef DDEBUG_H\n#define DDEBUG_H\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#if defined(DDEBUG) && (DDEBUG)\n\n#   define dd_dump_chain_size() { \\\n        int              n; \\\n        ngx_chain_t     *cl; \\\n            \\\n        for (n = 0, cl = u->out_bufs; cl; cl = cl->next, n++) { \\\n        } \\\n            \\\n        dd(\"chain size: %d\", n); \\\n    }\n\n#   if (NGX_HAVE_VARIADIC_MACROS)\n\n#       define dd(...) fprintf(stderr, \"drizzle *** %s: \", __func__); \\\n            fprintf(stderr, __VA_ARGS__); \\\n            fprintf(stderr, \" at %s line %d.\\n\", __FILE__, __LINE__)\n\n#   else\n\n#include <stdarg.h>\n#include <stdio.h>\n\n#include <stdarg.h>\n\nstatic ngx_inline void\ndd(const char * fmt, ...) {\n}\n\n#    endif\n\n#else\n\n#   define dd_dump_chain_size()\n\n#   if (NGX_HAVE_VARIADIC_MACROS)\n\n#       define dd(...)\n\n#   else\n\n#include <stdarg.h>\n\nstatic ngx_inline void\ndd(const char * fmt, ...) {\n}\n\n#   endif\n\n#endif\n\n#if defined(DDEBUG) && (DDEBUG)\n\n#define dd_check_read_event_handler(r)   \\\n    dd(\"r->read_event_handler = %s\", \\\n        r->read_event_handler == ngx_http_block_reading ? \\\n            \"ngx_http_block_reading\" : \\\n        r->read_event_handler == ngx_http_test_reading ? \\\n            \"ngx_http_test_reading\" : \\\n        r->read_event_handler == ngx_http_request_empty_handler ? \\\n            \"ngx_http_request_empty_handler\" : \"UNKNOWN\")\n\n#define dd_check_write_event_handler(r)   \\\n    dd(\"r->write_event_handler = %s\", \\\n        r->write_event_handler == ngx_http_handler ? \\\n            \"ngx_http_handler\" : \\\n        r->write_event_handler == ngx_http_core_run_phases ? \\\n            \"ngx_http_core_run_phases\" : \\\n        r->write_event_handler == ngx_http_request_empty_handler ? \\\n            \"ngx_http_request_empty_handler\" : \"UNKNOWN\")\n\n#else\n\n#define dd_check_read_event_handler(r)\n#define dd_check_write_event_handler(r)\n\n#endif\n\n#define dd_drizzle_result(result) \\\n    dd(\"drizzle result:     row_count=%\" PRId64 \"\\n\" \\\n         \"            insert_id=%\" PRId64 \"\\n\" \\\n         \"        warning_count=%u\\n\" \\\n         \"         column_count=%u\\n\\n\", \\\n         drizzle_result_row_count(result), \\\n         drizzle_result_insert_id(result), \\\n         drizzle_result_warning_count(result), \\\n         drizzle_result_column_count(result))\n\n#define dd_drizzle_column(column) \\\n    dd(\"drizzle column:   catalog=%s\\n\" \\\n         \"              db=%s\\n\" \\\n         \"           table=%s\\n\" \\\n         \"       org_table=%s\\n\" \\\n         \"            name=%s\\n\" \\\n         \"        org_name=%s\\n\" \\\n         \"         charset=%u\\n\" \\\n         \"            size=%u\\n\" \\\n         \"        max_size=%zu\\n\" \\\n         \"            type=%u\\n\" \\\n         \"           flags=%u\\n\\n\", \\\n         drizzle_column_catalog(column), drizzle_column_db(column), \\\n         drizzle_column_table(column), drizzle_column_orig_table(column), \\\n         drizzle_column_name(column), drizzle_column_orig_name(column), \\\n         drizzle_column_charset(column), drizzle_column_size(column), \\\n         drizzle_column_max_size(column), drizzle_column_type(column), \\\n         drizzle_column_flags(column));\n\n#endif /* DDEBUG_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_checker.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include \"ngx_http_drizzle_quoting.h\"\n\n\nngx_int_t\nngx_http_drizzle_check_int(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_check_float(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_check_bool(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_check_col(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_check_table(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_check_keyword(ngx_str_t *value, void *data)\n{\n    /* TODO */\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_checker.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_CHECKER_H\n#define NGX_HTTP_DRIZZLE_CHECKER_H\n\n#include \"ngx_http_drizzle_module.h\"\n\nngx_int_t ngx_http_drizzle_check_int(ngx_str_t *value, void *data);\n\nngx_int_t ngx_http_drizzle_check_float(ngx_str_t *value, void *data);\n\nngx_int_t ngx_http_drizzle_check_bool(ngx_str_t *value, void *data);\n\nngx_int_t ngx_http_drizzle_check_col(ngx_str_t *value, void *data);\n\nngx_int_t ngx_http_drizzle_check_table(ngx_str_t *value, void *data);\n\nngx_int_t ngx_http_drizzle_check_keyword(ngx_str_t *value, void *data);\n\n#endif /* NGX_HTTP_DRIZZLE_CHECKER_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_handler.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_handler.h\"\n#include \"ngx_http_drizzle_processor.h\"\n#include \"ngx_http_drizzle_util.h\"\n#include \"ngx_http_drizzle_upstream.h\"\n#include \"ngx_http_drizzle_keepalive.h\"\n\n\n#ifdef _WIN32\n/* import the POLLIN and POLLOUT flags */\n#   ifndef WIN32_LEAN_AND_MEAN\n#       define WIN32_LEAN_AND_MEAN\n#   endif\n#   include <winsock2.h>\n#endif\n\n\n/* for read/write event handlers */\n\n\nstatic ngx_int_t ngx_http_drizzle_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_drizzle_reinit_request(ngx_http_request_t *r);\nstatic void ngx_http_drizzle_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_drizzle_finalize_request(ngx_http_request_t *r,\n        ngx_int_t rc);\nstatic ngx_int_t ngx_http_drizzle_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_drizzle_input_filter_init(void *data);\nstatic ngx_int_t ngx_http_drizzle_input_filter(void *data, ssize_t bytes);\n\n\nngx_int_t\nngx_http_drizzle_handler(ngx_http_request_t *r)\n{\n    ngx_http_upstream_t            *u;\n    ngx_http_drizzle_loc_conf_t    *dlcf;\n    ngx_http_drizzle_ctx_t         *dctx;\n    ngx_http_core_loc_conf_t       *clcf;\n    ngx_str_t                       target;\n    ngx_url_t                       url;\n    ngx_connection_t               *c;\n\n    dd(\"request: %p\", r);\n    dd(\"subrequest in memory: %d\", (int) r->subrequest_in_memory);\n    dd(\"connection: %p\", r->connection);\n    dd(\"connection log: %p\", r->connection->log);\n\n    if (r->subrequest_in_memory) {\n        /* TODO: add support for subrequest in memory by\n         * emitting output into u->buffer instead */\n\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"ngx_http_drizzle_module does not support \"\n                      \"subrequest in memory\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_drizzle_module);\n\n    if ((dlcf->default_query == NULL) && !(dlcf->methods_set & r->method)) {\n        if (dlcf->methods_set != 0) {\n            return NGX_HTTP_NOT_ALLOWED;\n        }\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"drizzle: missing \\\"drizzle_query\\\" in location \\\"%V\\\"\",\n                      &clcf->name);\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    dd(\"XXX upstream already exists? %p\", r->upstream);\n\n#if defined(nginx_version) &&                                                \\\n    ((nginx_version >= 7063 && nginx_version < 8000)                         \\\n     || nginx_version >= 8007)\n\n    dd(\"creating upstream.......\");\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u = r->upstream;\n\n#else /* 0.7.x < 0.7.63, 0.8.x < 0.8.7 */\n\n    dd(\"XXX create upstream\");\n    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));\n    if (u == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u->peer.log = r->connection->log;\n    u->peer.log_error = NGX_ERROR_ERR;\n#  if (NGX_THREADS)\n    u->peer.lock = &r->connection->lock;\n#  endif\n\n    r->upstream = u;\n\n#endif\n\n    if (dlcf->complex_target) {\n        /* variables used in the drizzle_pass directive */\n        if (ngx_http_complex_value(r, dlcf->complex_target, &target)\n            != NGX_OK)\n        {\n            dd(\"failed to compile\");\n            return NGX_ERROR;\n        }\n\n        if (target.len == 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"drizzle: handler: empty \\\"drizzle_pass\\\" target\");\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        url.host = target;\n        url.port = 0;\n        url.no_resolve = 1;\n\n        dlcf->upstream.upstream = ngx_http_upstream_drizzle_add(r, &url);\n\n        if (dlcf->upstream.upstream == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"drizzle: upstream \\\"%V\\\" not found\", &target);\n\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    dctx = ngx_pcalloc(r->pool, sizeof(ngx_http_drizzle_ctx_t));\n    if (dctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_set_ctx(r, dctx, ngx_http_drizzle_module);\n\n    u->schema.len = sizeof(\"drizzle://\") - 1;\n    u->schema.data = (u_char *) \"drizzle://\";\n\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_drizzle_module;\n\n    dd(\"drizzle tag: %p\", (void *) u->output.tag);\n\n    u->conf = &dlcf->upstream;\n\n    u->create_request = ngx_http_drizzle_create_request;\n    u->reinit_request = ngx_http_drizzle_reinit_request;\n    u->process_header = ngx_http_drizzle_process_header;\n    u->abort_request = ngx_http_drizzle_abort_request;\n    u->finalize_request = ngx_http_drizzle_finalize_request;\n\n    /* we bypass the upstream input filter mechanism in\n     * ngx_http_upstream_process_headers */\n\n    u->input_filter_init = ngx_http_drizzle_input_filter_init;\n    u->input_filter = ngx_http_drizzle_input_filter;\n    u->input_filter_ctx = NULL;\n\n#if defined(nginx_version) && nginx_version >= 8011\n    r->main->count++;\n#endif\n\n    dd(\"XXX connect timeout: %d\", (int) dlcf->upstream.connect_timeout);\n\n    ngx_http_upstream_dbd_init(r);\n\n    /* override the read/write event handler to our own */\n    u->write_event_handler = ngx_http_drizzle_wev_handler;\n    u->read_event_handler  = ngx_http_drizzle_rev_handler;\n\n    /* a bit hack-ish way to return error response (clean-up part) */\n    if ((u->peer.connection) && (u->peer.connection->fd == 0)) {\n        c = u->peer.connection;\n        u->peer.connection = NULL;\n\n        if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n\n        ngx_free_connection(c);\n\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n#if defined(nginx_version) && (nginx_version >= 8017)\n            NGX_HTTP_SERVICE_UNAVAILABLE);\n#else\n            dctx->status ? dctx->status : NGX_HTTP_INTERNAL_SERVER_ERROR);\n#endif\n    }\n\n    return NGX_DONE;\n}\n\n\nvoid\nngx_http_drizzle_wev_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_connection_t            *c;\n\n    dd(\"drizzle wev handler\");\n\n    /* just to ensure u->reinit_request always gets called for\n     * upstream_next */\n    u->request_sent = 1;\n\n    c = u->peer.connection;\n\n    if (c->write->timedout) {\n        dd(\"drizzle connection write timeout\");\n\n        ngx_http_drizzle_set_thread_id_variable(r, u);\n\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n    if (ngx_http_upstream_drizzle_test_connect(c) != NGX_OK) {\n        dd(\"drizzle connection is broken\");\n\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    ngx_http_drizzle_set_libdrizzle_ready(r);\n\n    (void) ngx_http_drizzle_process_events(r);\n}\n\n\nvoid\nngx_http_drizzle_rev_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_connection_t            *c;\n\n    dd(\"drizzle rev handler\");\n\n    /* just to ensure u->reinit_request always gets called for\n     * upstream_next */\n    u->request_sent = 1;\n\n    c = u->peer.connection;\n\n    if (c->read->timedout) {\n        dd(\"drizzle connection read timeout\");\n        ngx_http_drizzle_set_thread_id_variable(r, u);\n\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n    if (ngx_http_upstream_drizzle_test_connect(c) != NGX_OK) {\n        dd(\"drizzle connection is broken\");\n\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    ngx_http_drizzle_set_libdrizzle_ready(r);\n\n    (void) ngx_http_drizzle_process_events(r);\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_create_request(ngx_http_request_t *r)\n{\n    r->upstream->request_bufs = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_upstream_t         *u;\n\n    u = r->upstream;\n\n    /* override the read/write event handler to our own */\n    u->write_event_handler = ngx_http_drizzle_wev_handler;\n    u->read_event_handler  = ngx_http_drizzle_rev_handler;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_drizzle_abort_request(ngx_http_request_t *r)\n{\n}\n\n\nstatic void\nngx_http_drizzle_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc)\n{\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_process_header(ngx_http_request_t *r)\n{\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"ngx_http_drizzle_process_header should not be called\"\n                  \" by the upstream\");\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_input_filter_init(void *data)\n{\n    ngx_http_request_t          *r = data;\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"ngx_http_drizzle_input_filter_init should not be called\"\n                  \" by the upstream\");\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_input_filter(void *data, ssize_t bytes)\n{\n    ngx_http_request_t          *r = data;\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"ngx_http_drizzle_input_filter should not be called\"\n                  \" by the upstream\");\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_http_drizzle_set_libdrizzle_ready(ngx_http_request_t *r)\n{\n    ngx_http_upstream_drizzle_peer_data_t       *dp;\n    drizzle_con_st                              *dc;\n#if 1\n    short                                        revents = 0;\n#endif\n\n    dp = ngx_http_drizzle_get_peer_data(r);\n\n    dc = dp->drizzle_con;\n\n#if 0\n    /* libdrizzle uses standard poll() event constants\n     * and depends on drizzle_con_wait() to set them.\n     * we can directly call drizzle_con_wait() here to\n     * set those drizzle internal event states, because\n     * epoll() and other underlying event mechamism used\n     * by the nginx core can play well enough with poll().\n     * */\n\n    (void) drizzle_con_wait(dc->drizzle);\n#endif\n\n#if 1\n    revents |= POLLOUT;\n    revents |= POLLIN;\n\n    /* drizzle_con_set_revents() isn't declared external in libdrizzle-0.4.0, */\n    /* so we have to do its job all by ourselves... */\n\n    dc->options |= DRIZZLE_CON_IO_READY;\n    dc->revents = revents;\n    dc->events &= (short) ~revents;\n#endif\n}\n\n\nngx_int_t\nngx_http_drizzle_status_handler(ngx_http_request_t *r)\n{\n    ngx_http_upstream_main_conf_t  *umcf;\n    ngx_http_upstream_srv_conf_t  **uscfp;\n    ngx_http_upstream_srv_conf_t   *uscf;\n    ngx_uint_t                      i, n;\n    ngx_chain_t                    *cl;\n    ngx_buf_t                      *b;\n    size_t                          len;\n    ngx_int_t                       rc;\n    ngx_queue_t                    *q;\n\n    ngx_http_drizzle_keepalive_cache_t      *item;\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    uscfp = umcf->upstreams.elts;\n\n    /* calculate the output buffer length */\n\n    len = 0;\n\n    if (ngx_process == NGX_PROCESS_WORKER) {\n        len += sizeof(\"worker process: \\n\\n\") - 1\n             + ngx_http_drizzle_get_num_size(ngx_pid);\n    }\n\n    n = 0;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n        uscf = uscfp[i];\n\n        if (uscf->srv_conf == NULL) {\n            /* skip implicit upstream specified directly by the fastcgi_pass,\n             * proxy_pass, and similar directives */\n\n            continue;\n        }\n\n        dscf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_drizzle_module);\n\n        if (dscf == NULL || dscf->servers == NULL) {\n            continue;\n        }\n\n        if (n != 0) {\n            len += sizeof(\"\\n\") - 1;\n        }\n\n        n++;\n\n        len += sizeof(\"upstream \\n\") - 1\n             + uscf->host.len\n             + sizeof(\"  active connections: \\n\") - 1\n             + ngx_http_drizzle_get_num_size(dscf->active_conns)\n             + sizeof(\"  connection pool capacity: \\n\") - 1\n             + ngx_http_drizzle_get_num_size(dscf->max_cached);\n\n        if (dscf->max_cached) {\n            /* dump overflow flag for the connection pool */\n\n            switch (dscf->overflow) {\n                case drizzle_keepalive_overflow_ignore:\n                    len += sizeof(\"  overflow: ignore\\n\") - 1;\n                    break;\n\n                case drizzle_keepalive_overflow_reject:\n                    len += sizeof(\"  overflow: reject\\n\") - 1;\n                    break;\n\n                default:\n                    len += sizeof(\"  overflow: N/A\\n\") - 1;\n                    break;\n            }\n\n            /* dump the lengths of the \"cache\" and \"free\" queues in the pool */\n\n            len += sizeof(\"  cached connection queue: \\n\") - 1\n                 + ngx_http_drizzle_get_num_size(\n                         ngx_http_drizzle_queue_size(&dscf->cache)\n                   )\n                 + sizeof(\"  free'd connection queue: \\n\") - 1\n                 + ngx_http_drizzle_get_num_size(\n                         ngx_http_drizzle_queue_size(&dscf->free)\n                   )\n\n            /* dump how many times that each individual connection in the\n             * pool has been successfully used in the \"cache\" queue */\n\n                 + sizeof(\"  cached connection successfully used count:\\n\") - 1;\n\n            for (q = ngx_queue_head(&dscf->cache);\n                 q != ngx_queue_sentinel(&dscf->cache);\n                 q = ngx_queue_next(q))\n            {\n                item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                      queue);\n\n                len += sizeof(\" \") - 1\n                     + ngx_http_drizzle_get_num_size(item->used);\n            }\n\n            /* dump how many times that each individual connection in the\n             * pool has been successfully used in the \"free\" queue */\n\n            len += sizeof(\"  free'd connection successfully used count:\\n\") - 1;\n\n            for (q = ngx_queue_head(&dscf->free);\n                 q != ngx_queue_sentinel(&dscf->free);\n                 q = ngx_queue_next(q))\n            {\n                item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                      queue);\n\n                len += sizeof(\" \") - 1\n                     + ngx_http_drizzle_get_num_size(item->used);\n            }\n        }\n\n        len += sizeof(\"  servers: \\n\") - 1\n             + ngx_http_drizzle_get_num_size(dscf->servers->nelts)\n             + sizeof(\"  peers: \\n\") - 1\n             + ngx_http_drizzle_get_num_size(dscf->peers->number);\n    }\n\n    /* allocate the output buffer */\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* fill in the output buffer with the actual data */\n\n    if (ngx_process == NGX_PROCESS_WORKER) {\n        b->last = ngx_sprintf(b->last, \"worker process: %P\\n\\n\", ngx_pid);\n    }\n\n    n = 0;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n        uscf = uscfp[i];\n\n        if (uscf->srv_conf == NULL) {\n            /* skip implicit upstream specified directly by the fastcgi_pass,\n             * proxy_pass, and similar directives */\n\n            continue;\n        }\n\n        dscf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_drizzle_module);\n\n        if (dscf == NULL || dscf->servers == NULL) {\n            continue;\n        }\n\n        if (n != 0) {\n            *b->last++ = '\\n';\n        }\n\n        n++;\n\n        b->last = ngx_copy_const_str(b->last, \"upstream \");\n        b->last = ngx_copy(b->last, uscf->host.data, uscf->host.len);\n\n        *b->last++ = '\\n';\n\n        b->last = ngx_sprintf(b->last, \"  active connections: %uD\\n\",\n                              dscf->active_conns);\n\n        b->last = ngx_sprintf(b->last, \"  connection pool capacity: %uD\\n\",\n                              dscf->max_cached);\n\n        if (dscf->max_cached) {\n            /* dump overflow flag for the connection pool */\n\n            switch (dscf->overflow) {\n                case drizzle_keepalive_overflow_ignore:\n                    b->last = ngx_copy_const_str(b->last,\n                                                 \"  overflow: ignore\\n\");\n                    break;\n\n                case drizzle_keepalive_overflow_reject:\n                    b->last = ngx_copy_const_str(b->last,\n                                                 \"  overflow: reject\\n\");\n                    break;\n\n                default:\n                    b->last = ngx_copy_const_str(b->last, \"  overflow: N/A\\n\");\n                    break;\n            }\n\n            /* dump the lengths of the \"cache\" and \"free\" queues in the pool */\n\n            b->last = ngx_sprintf(b->last, \"  cached connection queue: %uD\\n\",\n                                  ngx_http_drizzle_queue_size(&dscf->cache));\n\n            b->last = ngx_sprintf(b->last, \"  free'd connection queue: %uD\\n\",\n                                  ngx_http_drizzle_queue_size(&dscf->free));\n\n            /* dump how many times that each individual connection in the\n             * pool has been successfully used in the \"cache\" queue */\n\n            b->last = ngx_copy_const_str(b->last, \"  cached connection \"\n                                         \"successfully used count:\");\n\n            for (q = ngx_queue_head(&dscf->cache);\n                 q != ngx_queue_sentinel(&dscf->cache);\n                 q = ngx_queue_next(q))\n            {\n                item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                      queue);\n                b->last = ngx_sprintf(b->last, \" %uD\", item->used);\n            }\n\n            *b->last++ = '\\n';\n\n            /* dump how many times that each individual connection in the\n             * pool has been successfully used in the \"free\" queue */\n\n            b->last = ngx_copy_const_str(b->last, \"  free'd connection \"\n                                         \"successfully used count:\");\n\n            for (q = ngx_queue_head(&dscf->free);\n                 q != ngx_queue_sentinel(&dscf->free);\n                 q = ngx_queue_next(q))\n            {\n                item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                      queue);\n                b->last = ngx_sprintf(b->last, \" %uD\", item->used);\n            }\n\n            *b->last++ = '\\n';\n        }\n\n        b->last = ngx_sprintf(b->last, \"  servers: %uD\\n\",\n                              dscf->servers->nelts);\n\n        b->last = ngx_sprintf(b->last, \"  peers: %uD\\n\", dscf->peers->number);\n    }\n\n    if (b->last != b->end) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"drizzle_status output buffer error: %O != %O\",\n                      (off_t) (b->last - b->pos),\n                      (off_t) (b->end - b->pos));\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (r == r->main) {\n        b->last_buf = 1;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    r->headers_out.status = NGX_HTTP_OK;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return ngx_http_output_filter(r, cl);\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_handler.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_HANDLER_H\n#define NGX_HTTP_DRIZZLE_HANDLER_H\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nvoid ngx_http_drizzle_set_libdrizzle_ready(ngx_http_request_t *r);\n\nngx_int_t ngx_http_drizzle_handler(ngx_http_request_t *r);\n\nvoid ngx_http_drizzle_rev_handler(ngx_http_request_t *r,\n        ngx_http_upstream_t *u);\n\nvoid ngx_http_drizzle_wev_handler(ngx_http_request_t *r,\n        ngx_http_upstream_t *u);\n\nngx_int_t ngx_http_drizzle_status_handler(ngx_http_request_t *r);\n\n\n#endif /* NGX_HTTP_DRIZZLE_HANDLER_H */\n"
  },
  {
    "path": "src/ngx_http_drizzle_keepalive.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_keepalive.h\"\n#include \"ngx_http_drizzle_util.h\"\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\nstatic void ngx_http_drizzle_keepalive_dummy_handler(ngx_event_t *ev);\nstatic void ngx_http_drizzle_keepalive_close_handler(ngx_event_t *ev);\n\n\nchar *\nngx_http_upstream_drizzle_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_upstream_drizzle_srv_conf_t        *dscf = conf;\n    ngx_str_t                                   *value;\n    ngx_uint_t                                   i;\n    ngx_int_t                                    n;\n    u_char                                      *data;\n    ngx_uint_t                                   len;\n\n    if (dscf->max_cached) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_http_drizzle_strcmp_const(value[i].data, \"max=\")\n            == 0)\n        {\n            len = value[i].len - (sizeof(\"max=\") - 1);\n            data = &value[i].data[sizeof(\"max=\") - 1];\n\n            n = ngx_atoi(data, len);\n\n            if (n == NGX_ERROR || n < 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid \\\"max\\\" value \\\"%V\\\" \"\n                                   \"in \\\"%V\\\" directive\",\n                                   &value[i], &cmd->name);\n\n                return NGX_CONF_ERROR;\n            }\n\n            dscf->max_cached = n;\n\n            continue;\n        }\n\n        if (ngx_http_drizzle_strcmp_const(value[i].data, \"mode=\") == 0) {\n            len = value[i].len - (sizeof(\"mode=\") - 1);\n            data = &value[i].data[sizeof(\"mode=\") - 1];\n\n            switch (len) {\n            case 6:\n                if (ngx_http_drizzle_strcmp_const(data, \"single\") == 0) {\n                    dscf->single = 1;\n                }\n                break;\n\n            case 5:\n                if (ngx_http_drizzle_strcmp_const(data, \"multi\") == 0) {\n                    dscf->single = 0;\n                }\n                break;\n\n            default:\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"drizzle: invalid \\\"mode\\\" value \\\"%V\\\" \"\n                                   \"in \\\"%V\\\" directive\",\n                                   &value[i], &cmd->name);\n\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_http_drizzle_strcmp_const(value[i].data, \"overflow=\") == 0) {\n            len = value[i].len - (sizeof(\"overflow=\") - 1);\n            data = &value[i].data[sizeof(\"overflow=\") - 1];\n\n            switch (len) {\n            case 6:\n                if (ngx_http_drizzle_strcmp_const(data, \"reject\") == 0) {\n                    dscf->overflow = drizzle_keepalive_overflow_reject;\n                } else if (ngx_http_drizzle_strcmp_const(data, \"ignore\") == 0) {\n                    dscf->overflow = drizzle_keepalive_overflow_ignore;\n                }\n                break;\n\n            default:\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"drizzle: invalid \\\"overflow\\\" value \\\"%V\\\" \"\n                                   \"in \\\"%V\\\" directive\",\n                                   &value[i], &cmd->name);\n\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"drizzle: invalid parameter \\\"%V\\\" in\"\n                           \" \\\"%V\\\" directive\",\n                           &value[i], &cmd->name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_keepalive_init(ngx_pool_t *pool,\n    ngx_http_upstream_drizzle_srv_conf_t *dscf)\n{\n    ngx_uint_t                              i;\n    ngx_http_drizzle_keepalive_cache_t     *cached;\n\n    /* allocate cache items and add to free queue */\n\n    cached = ngx_pcalloc(pool,\n                         sizeof(ngx_http_drizzle_keepalive_cache_t)\n                         * dscf->max_cached);\n    if (cached == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_queue_init(&dscf->cache);\n    ngx_queue_init(&dscf->free);\n\n    for (i = 0; i < dscf->max_cached; i++) {\n        ngx_queue_insert_head(&dscf->free, &cached[i].queue);\n        cached[i].srv_conf = dscf;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_keepalive_get_peer_single(ngx_peer_connection_t *pc,\n    ngx_http_upstream_drizzle_peer_data_t *dp,\n    ngx_http_upstream_drizzle_srv_conf_t *dscf)\n{\n    ngx_http_drizzle_keepalive_cache_t      *item;\n    ngx_queue_t                             *q;\n    ngx_connection_t                        *c;\n\n    if (!ngx_queue_empty(&dscf->cache)) {\n        dd(\"getting cached mysql connection...\");\n\n        q = ngx_queue_head(&dscf->cache);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t, queue);\n        c = item->connection;\n\n        ngx_queue_insert_head(&dscf->free, q);\n\n        c->idle = 0;\n        c->log = pc->log;\n        c->read->log = pc->log;\n        c->write->log = pc->log;\n\n        dp->name.data = item->name.data;\n        dp->name.len = item->name.len;\n\n        dp->sockaddr = item->sockaddr;\n        dp->drizzle_con = item->drizzle_con;\n        dp->has_set_names = item->has_set_names;\n        dp->used = item->used;\n\n        pc->connection = c;\n        pc->cached = 1;\n        pc->name = &dp->name;\n        pc->sockaddr = &dp->sockaddr;\n        pc->socklen = item->socklen;\n\n        return NGX_DONE;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_drizzle_keepalive_get_peer_multi(ngx_peer_connection_t *pc,\n    ngx_http_upstream_drizzle_peer_data_t *dp,\n    ngx_http_upstream_drizzle_srv_conf_t *dscf)\n{\n    ngx_queue_t                             *q, *cache;\n    ngx_http_drizzle_keepalive_cache_t      *item;\n    ngx_connection_t                        *c;\n\n    /* search cache for suitable connection */\n\n    cache = &dscf->cache;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t, queue);\n        c = item->connection;\n\n        /* XXX maybe we should take dbname and user into account\n         * as well? */\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            ngx_queue_remove(q);\n            ngx_queue_insert_head(&dscf->free, q);\n\n            c->idle = 0;\n            c->log = pc->log;\n            c->read->log = pc->log;\n            c->write->log = pc->log;\n\n            pc->connection = c;\n            pc->cached = 1;\n\n            /* we do not need to resume dp->name here because\n             * it already takes the right value in the\n             * ngx_http_upstream_drizzle_get_peer function */\n\n            dp->drizzle_con = item->drizzle_con;\n            dp->has_set_names = item->has_set_names;\n            dp->used = item->used;\n\n            return NGX_DONE;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nvoid\nngx_http_drizzle_keepalive_free_peer(ngx_peer_connection_t *pc,\n    ngx_http_upstream_drizzle_peer_data_t *dp,\n    ngx_http_upstream_drizzle_srv_conf_t *dscf, ngx_uint_t state)\n{\n    ngx_uint_t                               status;\n    ngx_http_drizzle_keepalive_cache_t      *item;\n    ngx_queue_t                             *q;\n    ngx_connection_t                        *c;\n    ngx_http_upstream_t                     *u;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"drizzle: free keepalive peer\");\n\n    if (state & NGX_PEER_FAILED) {\n        dp->failed = 1;\n    }\n\n    u = dp->upstream;\n    status = u->headers_in.status_n;\n\n    dd(\"dp failed: %d\", (int) dp->failed);\n    dd(\"pc->connection: %p\", pc->connection);\n    dd(\"status = %d\", (int) status);\n\n    if (!dp->failed\n        && pc->connection != NULL\n        && (status == NGX_HTTP_NOT_FOUND\n            || status == NGX_HTTP_GONE\n            || (status == NGX_HTTP_OK && u->header_sent && u->length == 0)))\n    {\n        c = pc->connection;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"drizzle: free keepalive peer: saving connection %p\",\n                       c);\n\n        if (ngx_queue_empty(&dscf->free)) {\n            /* connection pool is already full */\n\n            dd(\"caching connection forcibly and the pool is already full\");\n\n            q = ngx_queue_last(&dscf->cache);\n            ngx_queue_remove(q);\n\n            item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                  queue);\n\n            ngx_http_upstream_drizzle_free_connection(pc->log, item->connection,\n                                                      item->drizzle_con, dscf);\n\n        } else {\n            dd(\"caching idle connection to the pool\");\n\n            q = ngx_queue_head(&dscf->free);\n            ngx_queue_remove(q);\n\n            item = ngx_queue_data(q, ngx_http_drizzle_keepalive_cache_t,\n                                  queue);\n        }\n\n        item->connection = c;\n        ngx_queue_insert_head(&dscf->cache, q);\n\n        pc->connection = NULL;\n\n        if (c->read->timer_set) {\n            ngx_del_timer(c->read);\n        }\n\n        if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n\n        c->write->handler = ngx_http_drizzle_keepalive_dummy_handler;\n        c->read->handler = ngx_http_drizzle_keepalive_close_handler;\n\n        c->data = item;\n        c->idle = 1;\n        c->log = ngx_cycle->log;\n        c->read->log = ngx_cycle->log;\n        c->write->log = ngx_cycle->log;\n\n        item->socklen = pc->socklen;\n        ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);\n\n        item->drizzle_con = dp->drizzle_con;\n        item->has_set_names = dp->has_set_names;\n\n        item->name.data = dp->name.data;\n        item->name.len = dp->name.len;\n\n        item->used = ++dp->used;\n    }\n}\n\n\nstatic void\nngx_http_drizzle_keepalive_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"drizzle: keepalive dummy handler\");\n}\n\n\nstatic void\nngx_http_drizzle_keepalive_close_handler(ngx_event_t *ev)\n{\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n    ngx_http_drizzle_keepalive_cache_t      *item;\n\n    int                n;\n    char               buf[1];\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"drizzle: keepalive close handler\");\n\n    c = ev->data;\n\n    if (c->close) {\n        goto close;\n    }\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {\n        /* stale event */\n\n#if 0\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            goto close;\n        }\n#endif\n\n        return;\n    }\n\nclose:\n\n    item = c->data;\n    dscf = item->srv_conf;\n\n    dd(\"closing fd %d\", c->fd);\n\n    ngx_http_upstream_drizzle_free_connection(ev->log, c, item->drizzle_con,\n                                              dscf);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_insert_head(&dscf->free, &item->queue);\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_keepalive.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_KEEPALIVE_H\n#define NGX_HTTP_DRIZZLE_KEEPALIVE_H\n\n\n#include \"ngx_http_drizzle_upstream.h\"\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\ntypedef struct {\n    ngx_http_upstream_drizzle_srv_conf_t  *srv_conf;\n\n    ngx_queue_t                          queue;\n\n    ngx_connection_t                    *connection;\n\n    socklen_t                            socklen;\n    struct sockaddr                      sockaddr;\n    drizzle_con_st                      *drizzle_con;\n    ngx_str_t                            name;\n\n    /* how many times this connection has been successfully used */\n    ngx_uint_t                           used;\n\n    unsigned                             has_set_names:1;\n\n} ngx_http_drizzle_keepalive_cache_t;\n\n\nchar *ngx_http_upstream_drizzle_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n        void *conf);\n\nngx_int_t ngx_http_drizzle_keepalive_init(ngx_pool_t *pool,\n        ngx_http_upstream_drizzle_srv_conf_t *dscf);\n\nngx_int_t ngx_http_drizzle_keepalive_get_peer_single(ngx_peer_connection_t *pc,\n        ngx_http_upstream_drizzle_peer_data_t *dp,\n        ngx_http_upstream_drizzle_srv_conf_t *dscf);\n\nngx_int_t ngx_http_drizzle_keepalive_get_peer_multi(ngx_peer_connection_t *pc,\n        ngx_http_upstream_drizzle_peer_data_t *dp,\n        ngx_http_upstream_drizzle_srv_conf_t *dscf);\n\nvoid ngx_http_drizzle_keepalive_free_peer(ngx_peer_connection_t *pc,\n        ngx_http_upstream_drizzle_peer_data_t *dp,\n        ngx_http_upstream_drizzle_srv_conf_t *dscf,\n        ngx_uint_t  state);\n\n#endif /* NGX_HTTP_DRIZZLE_KEEPALIVE_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_module.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_handler.h\"\n#include \"ngx_http_drizzle_upstream.h\"\n#include \"ngx_http_drizzle_keepalive.h\"\n\n\nstatic ngx_str_t  ngx_http_drizzle_tid_var_name =\n        ngx_string(\"drizzle_thread_id\");\n\n\n/* Forward declaration */\n\nstatic char *ngx_http_drizzle_set_complex_value_slot(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_drizzle_query(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_drizzle_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_drizzle_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_drizzle_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_drizzle_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_drizzle_tid_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic char *ngx_http_drizzle_enable_status(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\n\nstatic ngx_http_variable_t ngx_http_drizzle_variables[] = {\n\n    { ngx_string(\"drizzle_thread_id\"), NULL,\n      ngx_http_drizzle_tid_variable, 0,\n      NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\n/* config directives for module drizzle */\nstatic ngx_command_t ngx_http_drizzle_cmds[] = {\n    { ngx_string(\"drizzle_server\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_drizzle_server,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"drizzle_keepalive\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_drizzle_keepalive,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"drizzle_query\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_1MORE,\n      ngx_http_drizzle_query,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"drizzle_dbname\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_drizzle_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, dbname),\n      NULL },\n\n    { ngx_string(\"drizzle_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_drizzle_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"drizzle_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"drizzle_send_query_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"drizzle_recv_cols_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, recv_cols_timeout),\n      NULL },\n\n    { ngx_string(\"drizzle_recv_rows_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, recv_rows_timeout),\n      NULL },\n\n    { ngx_string(\"drizzle_module_header\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n          |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, enable_module_header),\n      NULL },\n\n    { ngx_string(\"drizzle_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n          |NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_drizzle_loc_conf_t, buf_size),\n      NULL },\n\n    { ngx_string(\"drizzle_status\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,\n      ngx_http_drizzle_enable_status,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\n/* Nginx HTTP subsystem module hooks */\nstatic ngx_http_module_t ngx_http_drizzle_module_ctx = {\n    NULL,    /* preconfiguration */\n    NULL,    /* postconfiguration */\n\n    NULL,    /* create_main_conf */\n    NULL,    /* merge_main_conf */\n\n    ngx_http_upstream_drizzle_create_srv_conf,\n             /* create_srv_conf */\n    NULL,    /* merge_srv_conf */\n\n    ngx_http_drizzle_create_loc_conf,    /* create_loc_conf */\n    ngx_http_drizzle_merge_loc_conf      /* merge_loc_conf */\n};\n\n\nngx_module_t ngx_http_drizzle_module = {\n    NGX_MODULE_V1,\n    &ngx_http_drizzle_module_ctx,       /* module context */\n    ngx_http_drizzle_cmds,              /* module directives */\n    NGX_HTTP_MODULE,                    /* module type */\n    NULL,    /* init master */\n    NULL,    /* init module */\n    NULL,    /* init process */\n    NULL,    /* init thread */\n    NULL,    /* exit thread */\n    NULL,    /* exit process */\n    NULL,    /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nngx_drizzle_http_method_t ngx_drizzle_http_methods[] = {\n    { (u_char *) \"GET\",       (uint32_t) NGX_HTTP_GET },\n    { (u_char *) \"HEAD\",      (uint32_t) NGX_HTTP_HEAD },\n    { (u_char *) \"POST\",      (uint32_t) NGX_HTTP_POST },\n    { (u_char *) \"PUT\",       (uint32_t) NGX_HTTP_PUT },\n    { (u_char *) \"DELETE\",    (uint32_t) NGX_HTTP_DELETE },\n    { (u_char *) \"MKCOL\",     (uint32_t) NGX_HTTP_MKCOL },\n    { (u_char *) \"COPY\",      (uint32_t) NGX_HTTP_COPY },\n    { (u_char *) \"MOVE\",      (uint32_t) NGX_HTTP_MOVE },\n    { (u_char *) \"OPTIONS\",   (uint32_t) NGX_HTTP_OPTIONS },\n    { (u_char *) \"PROPFIND\" , (uint32_t) NGX_HTTP_PROPFIND },\n    { (u_char *) \"PROPPATCH\", (uint32_t) NGX_HTTP_PROPPATCH },\n    { (u_char *) \"LOCK\",      (uint32_t) NGX_HTTP_LOCK },\n    { (u_char *) \"UNLOCK\",    (uint32_t) NGX_HTTP_UNLOCK },\n#if defined(nginx_version) && (nginx_version >= 8041)\n    { (u_char *) \"PATCH\",     (uint32_t) NGX_HTTP_PATCH },\n#endif\n    { NULL, 0 }\n};\n\n\nstatic void *\nngx_http_drizzle_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_drizzle_loc_conf_t             *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_drizzle_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->recv_cols_timeout = NGX_CONF_UNSET_MSEC;\n    conf->recv_rows_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->enable_module_header = NGX_CONF_UNSET;\n\n    /* the hardcoded values */\n    conf->upstream.cyclic_temp_file = 0;\n    conf->upstream.buffering = 0;\n    conf->upstream.ignore_client_abort = 0;\n    conf->upstream.send_lowat = 0;\n    conf->upstream.bufs.num = 0;\n    conf->upstream.busy_buffers_size = 0;\n    conf->upstream.max_temp_file_size = 0;\n    conf->upstream.temp_file_write_size = 0;\n    conf->upstream.intercept_errors = 1;\n    conf->upstream.intercept_404 = 1;\n    conf->upstream.pass_request_headers = 0;\n    conf->upstream.pass_request_body = 0;\n\n    /* set by ngx_pcalloc:\n     *      conf->dbname = NULL\n     *      conf->query  = NULL\n     */\n\n    conf->complex_target = NGX_CONF_UNSET_PTR;\n\n    conf->buf_size = NGX_CONF_UNSET_SIZE;\n    conf->tid_var_index = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_drizzle_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_drizzle_loc_conf_t *prev = parent;\n    ngx_http_drizzle_loc_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable_module_header,\n                         prev->enable_module_header, 1);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->recv_cols_timeout,\n                              prev->recv_cols_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->recv_rows_timeout,\n                              prev->recv_rows_timeout, 60000);\n\n    if (conf->dbname == NULL) {\n        conf->dbname = prev->dbname;\n    }\n\n    if ((conf->default_query == NULL) && (conf->queries == NULL)) {\n        conf->default_query = prev->default_query;\n        conf->methods_set = prev->methods_set;\n        conf->queries = prev->queries;\n    }\n\n    ngx_conf_merge_size_value(conf->buf_size, prev->buf_size,\n                              (size_t) ngx_pagesize);\n\n    if (conf->tid_var_index == NGX_CONF_UNSET) {\n        conf->tid_var_index = prev->tid_var_index;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_drizzle_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char                             *p = conf;\n    ngx_http_complex_value_t        **field;\n    ngx_str_t                        *value;\n    ngx_http_compile_complex_value_t  ccv;\n\n    field = (ngx_http_complex_value_t **) (p + cmd->offset);\n\n    if (*field) {\n        return \"is duplicate\";\n    }\n\n    *field = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n    if (*field == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        ngx_memzero(*field, sizeof(ngx_http_complex_value_t));\n        return NGX_OK;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = *field;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_drizzle_query(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                         *value = cf->args->elts;\n    ngx_str_t                          sql = value[cf->args->nelts - 1];\n    ngx_http_drizzle_loc_conf_t       *dlcf = conf;\n    ngx_http_compile_complex_value_t   ccv;\n    ngx_drizzle_mixed_t               *query;\n    ngx_drizzle_http_method_t         *method;\n    ngx_uint_t                         methods, i;\n\n    if (sql.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"drizzle: empty value in \\\"%V\\\" directive\",\n                           &cmd->name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 2) {\n        /* default query */\n        dd(\"default query\");\n\n        if (dlcf->default_query != NULL) {\n            return \"is duplicate\";\n        }\n\n        dlcf->default_query = ngx_pcalloc(cf->pool,\n                                          sizeof(ngx_drizzle_mixed_t));\n        if (dlcf->default_query == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        methods = 0xFFFF;\n        query = dlcf->default_query;\n\n    } else {\n        /* method-specific query */\n        dd(\"method-specific query\");\n\n        methods = 0;\n\n        for (i = 1; i < cf->args->nelts - 1; i++) {\n            for (method = ngx_drizzle_http_methods; method->name; method++) {\n                if (ngx_strcasecmp(value[i].data, method->name) == 0) {\n                    /* correct method name */\n                    if (dlcf->methods_set & method->key) {\n                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                           \"drizzle: \\\"%V\\\" directive\"\n                                           \" for method \\\"%V\\\" is duplicate\",\n                                           &cmd->name, &value[i]);\n\n                        return NGX_CONF_ERROR;\n                    }\n\n                    methods |= method->key;\n                    goto next;\n                }\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"drizzle: invalid method \\\"%V\\\"\", &value[i]);\n\n            return NGX_CONF_ERROR;\n\nnext:\n\n            continue;\n        }\n\n        if (dlcf->queries == NULL) {\n            dlcf->queries = ngx_array_create(cf->pool, 4,\n                                             sizeof(ngx_drizzle_mixed_t));\n            if (dlcf->queries == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        query = ngx_array_push(dlcf->queries);\n        if (query == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(query, sizeof(ngx_drizzle_mixed_t));\n\n        dlcf->methods_set |= methods;\n    }\n\n    if (ngx_http_script_variables_count(&sql)) {\n        /* complex value */\n        dd(\"complex value\");\n\n        query->key = methods;\n\n        query->cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (query->cv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &sql;\n        ccv.complex_value = query->cv;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    } else {\n        /* simple value */\n        dd(\"simple value\");\n\n        query->key = methods;\n        query->sv = sql;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_drizzle_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_drizzle_loc_conf_t             *dlcf = conf;\n\n    ngx_http_core_loc_conf_t                *clcf;\n    ngx_str_t                               *value;\n    ngx_http_compile_complex_value_t         ccv;\n    ngx_url_t                                url;\n    ngx_uint_t                               n;\n\n    if (dlcf->upstream.upstream) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_http_drizzle_add_variables(cf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    dlcf->tid_var_index = ngx_http_get_variable_index(cf,\n                                              &ngx_http_drizzle_tid_var_name);\n\n    if (dlcf->tid_var_index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_drizzle_handler;\n\n    if (clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    value = cf->args->elts;\n\n    n = ngx_http_script_variables_count(&value[1]);\n    if (n) {\n        dlcf->complex_target = ngx_palloc(cf->pool,\n                                          sizeof(ngx_http_complex_value_t));\n        if (dlcf->complex_target == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = dlcf->complex_target;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    dlcf->complex_target = NULL;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    url.url = value[1];\n    url.no_resolve = 1;\n\n    dlcf->upstream.upstream = ngx_http_upstream_add(cf, &url, 0);\n\n    if (dlcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t *var, *v;\n\n    for (v = ngx_http_drizzle_variables; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_tid_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->len = 0;\n    v->data = (u_char *) \"\";\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_drizzle_enable_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t                *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_drizzle_status_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_module.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef NGX_HTTP_DRIZZLE_MODULE_H\n#define NGX_HTTP_DRIZZLE_MODULE_H\n\n\n#include <ngx_config.h>\n#include <nginx.h>\n#include <ngx_http.h>\n\n\n/* XXX nginx undefines \"bool\", which breaks the libdrizzle 1.0 API\n * which makes use of \"bool\" */\n#if defined(__GNUC__)\n#   ifndef bool\n#       define bool _Bool\n#   endif\n#endif\n\n#include <libdrizzle/drizzle_client.h>\n\n\n#ifdef _WIN32\n/* remove the bad macros defined in libdrizzle/drizzle.h */\n#   undef close\n#   undef snprintf\n#endif\n\n\n#ifndef NGX_HTTP_GONE\n#define NGX_HTTP_GONE  410\n#endif\n\n#define ngx_http_drizzle_module_version  1012\n#define ngx_http_drizzle_module_version_string  \"0.1.12\"\n\n\nextern ngx_module_t ngx_http_drizzle_module;\n\n\ntypedef struct {\n    ngx_uint_t                          key;\n    ngx_str_t                           sv;\n    ngx_http_complex_value_t           *cv;\n} ngx_drizzle_mixed_t;\n\n\ntypedef struct {\n    u_char                             *name;\n    uint32_t                            key;\n} ngx_drizzle_http_method_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t             upstream;\n\n    /* drizzle database name */\n    ngx_http_complex_value_t            *dbname;\n\n    /* SQL query to be executed */\n    ngx_drizzle_mixed_t                 *default_query;\n    ngx_uint_t                           methods_set;\n    ngx_array_t                         *queries;\n\n    ngx_msec_t                           recv_cols_timeout;\n    ngx_msec_t                           recv_rows_timeout;\n\n    ngx_flag_t                           enable_module_header;\n\n    /* for quoting */\n    ngx_array_t                         *vars_to_quote;\n                /* of ngx_http_drizzle_var_to_quote_t */\n\n    ngx_array_t                         *user_types;\n                /* of ngx_http_drizzle_var_type_t */\n\n    ngx_http_complex_value_t            *complex_target;\n\n    size_t                               buf_size;\n\n    ngx_int_t                            tid_var_index; /* thread id variable\n                                                           index */\n} ngx_http_drizzle_loc_conf_t;\n\n\ntypedef struct {\n#if defined(nginx_version) && (nginx_version < 8017)\n    ngx_int_t                           status;\n#endif\n    /* original drizzle peer data; saved here so handlers can find it\n     * even when r->upstream->peer.data has been wrapped by another\n     * module such as the standard upstream keepalive (which became\n     * implicit for explicit upstreams in nginx 1.29.7) */\n    void                               *peer_data;\n} ngx_http_drizzle_ctx_t;\n\n\n/* states for the drizzle client state machine */\ntypedef enum {\n    state_db_connect,\n    state_db_send_query,\n    state_db_recv_cols,\n    state_db_recv_rows,\n    state_db_idle\n\n} ngx_http_drizzle_state_t;\n\n\n#endif /* NGX_HTTP_DRIZZLE_MODULE_H */\n"
  },
  {
    "path": "src/ngx_http_drizzle_output.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_output.h\"\n#include \"ngx_http_drizzle_processor.h\"\n#include \"ngx_http_drizzle_util.h\"\n#include \"resty_dbd_stream.h\"\n#include <nginx.h>\n\n\n#define ngx_http_drizzle_module_header_key \"X-Resty-DBD-Module\"\n\n#define ngx_http_drizzle_module_header_key_len                               \\\n    (sizeof(ngx_http_drizzle_module_header_key) - 1)\n\n#define ngx_http_drizzle_module_header_val                                   \\\n    \"ngx_drizzle \"                                                           \\\n      ngx_http_drizzle_module_version_string\n\n#define ngx_http_drizzle_module_header_val_len                               \\\n    (sizeof(ngx_http_drizzle_module_header_val) - 1)\n\n#define ngx_http_drizzle_module_header_key_len                               \\\n    (sizeof(ngx_http_drizzle_module_header_key) - 1)\n\n\nstatic ngx_int_t ngx_http_drizzle_get_buf(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp);\nstatic u_char *ngx_http_drizzle_get_postponed(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len);\nstatic rds_col_type_t ngx_http_drizzle_std_col_type(\n    drizzle_column_type_t col_type);\nstatic u_char *ngx_http_drizzle_request_mem(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len);\nstatic ngx_int_t ngx_http_drizzle_submit_mem(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len);\n\n\nngx_int_t\nngx_http_drizzle_output_result_header(ngx_http_request_t *r,\n    drizzle_result_st *res)\n{\n    u_char                          *pos, *last;\n    ngx_int_t                        rc;\n    ngx_http_upstream_t             *u = r->upstream;\n    const char                      *errstr;\n    size_t                           size;\n    uint16_t                         errstr_len;\n    uint16_t                         col_count;\n    uint16_t                         errcode;\n\n    ngx_http_upstream_drizzle_peer_data_t   *dp = ngx_http_drizzle_get_peer_data(r);\n\n    errcode = drizzle_result_error_code(res);\n\n    if (dp->enable_charset && ! dp->has_set_names) {\n        if (errcode != 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"drizzle: FATAL: failed to set names 'utf8' \"\n                          \"(error %d)\", (int) errcode);\n            return NGX_ERROR;\n        }\n\n        if (dp->drizzle_con && dp->drizzle_res.con) {\n            dd(\"before drizzle result free\");\n            dd(\"%p vs. %p\", dp->drizzle_res.con, dp->drizzle_con);\n\n            drizzle_result_free(&dp->drizzle_res);\n\n            dd(\"after drizzle result free\");\n        }\n\n        /* ngx_http_upstream_drizzle_done(r, u, dp, NGX_OK); */\n        dd(\"returning DONE when set names\");\n        return NGX_DONE;\n    }\n\n    errstr = drizzle_result_error(res);\n\n    errstr_len = (uint16_t) ngx_strlen(errstr);\n\n    col_count = drizzle_result_column_count(res);\n\n    size = sizeof(uint8_t)        /* endian type */\n           + sizeof(uint32_t)     /* format version */\n           + sizeof(uint8_t)      /* result type */\n\n           + sizeof(uint16_t)     /* standard error code */\n           + sizeof(uint16_t)     /* driver-specific error code */\n\n           + sizeof(uint16_t)     /* driver-specific errstr len */\n           + errstr_len           /* driver-specific errstr data */\n           + sizeof(uint64_t)     /* rows affected */\n           + sizeof(uint64_t)     /* insert id */\n           + sizeof(uint16_t);    /* column count */\n\n    pos = ngx_http_drizzle_request_mem(r, dp, size);\n    if (pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = pos;\n\n#if NGX_HAVE_LITTLE_ENDIAN\n    *last++ = 0;\n#else /* big endian */\n    *last++ = 1;\n#endif\n\n    /* RDS format version */\n\n    *(uint32_t *) last = (uint32_t) resty_dbd_stream_version;\n    last += sizeof(uint32_t);\n\n    /* result type fixed to 0 */\n    *last++ = 0;\n\n    /* standard error code\n     * FIXME: define the standard error code set and map\n     * libdrizzle's to it. */\n    *(uint16_t *) last = errcode;\n    last += sizeof(uint16_t);\n\n     /* driver-specific error code */\n    *(uint16_t *) last = drizzle_result_error_code(res);\n    last += sizeof(uint16_t);\n\n    /* driver-specific errstr len */\n    *(uint16_t *) last = errstr_len;\n    last += sizeof(uint16_t);\n\n    /* driver-specific errstr data */\n    if (errstr_len) {\n        last = ngx_copy(last, (u_char *) errstr, errstr_len);\n    }\n\n    /* affected rows */\n    *(uint64_t *) last = drizzle_result_affected_rows(res);\n    last += sizeof(uint64_t);\n\n    /* insert id */\n    *(uint64_t *) last = drizzle_result_insert_id(res);\n    last += sizeof(uint64_t);\n\n    /* column count */\n    *(uint16_t *) last = col_count;\n    last += sizeof(uint16_t);\n\n    if ((size_t) (last - pos) != size) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"drizzle: FATAL: output result header buffer error\");\n        return NGX_ERROR;\n    }\n\n    if (col_count == 0) {\n        dd(\"Col count is ZERO\");\n\n        /* we suppress row terminator here when there's no columns */\n        dp->seen_stream_end = 1;\n\n        rc = ngx_http_drizzle_submit_mem(r, dp, size);\n\n        if (rc != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        dd(\"about to be done...\");\n        ngx_http_upstream_drizzle_done(r, u, dp, NGX_DONE);\n        dd(\"i am returning DONE\");\n\n        return NGX_DONE;\n    }\n\n    return ngx_http_drizzle_submit_mem(r, dp, size);\n}\n\n\nngx_int_t\nngx_http_drizzle_output_bufs(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp)\n{\n    ngx_http_upstream_t                    *u = r->upstream;\n    ngx_int_t                               rc;\n    ngx_str_t                               key, value;\n    ngx_http_drizzle_loc_conf_t            *dlcf;\n    ngx_chain_t                            *cl;\n\n    dd(\"enter\");\n    dd_dump_chain_size();\n\n    if (dp->seen_stream_end) {\n        dp->seen_stream_end = 0;\n\n        if (dp->avail_out) {\n            cl = ngx_alloc_chain_link(r->pool);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf = dp->out_buf;\n            cl->next = NULL;\n            *dp->last_out = cl;\n            dp->last_out = &cl->next;\n\n            dp->avail_out = 0;\n        }\n    }\n\n    if (!u->header_sent && u->out_bufs) {\n        ngx_http_clear_content_length(r);\n\n        r->headers_out.status = NGX_HTTP_OK;\n\n        /* set the Content-Type header */\n        r->headers_out.content_type.data = (u_char *) rds_content_type;\n        r->headers_out.content_type.len = rds_content_type_len;\n        r->headers_out.content_type_len = rds_content_type_len;\n        r->headers_out.content_type_lowcase = NULL;\n\n        dlcf = ngx_http_get_module_loc_conf(r, ngx_http_drizzle_module);\n\n        if (dlcf->enable_module_header) {\n            /* set the X-Resty-DBD-Module header */\n\n            key.data = (u_char *) ngx_http_drizzle_module_header_key;\n            key.len  = ngx_http_drizzle_module_header_key_len;\n\n            value.data = (u_char *) ngx_http_drizzle_module_header_val;\n            value.len = ngx_http_drizzle_module_header_val_len;\n\n            rc = ngx_http_drizzle_set_header(r, &key, &value);\n\n            if (rc != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        rc = ngx_http_send_header(r);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        u->header_sent = 1;\n    }\n\n\n    for ( ;; ) {\n        if (u->out_bufs == NULL) {\n            return NGX_OK;\n        }\n\n#if 0\n        {\n            int              n;\n            ngx_chain_t     *cl;\n\n            for (n = 0, cl = u->out_bufs; cl; cl = cl->next, n++) {\n            }\n\n            fprintf(stderr, \"XXX chain size: %d\\n\", n);\n        }\n#endif\n\n        rc = ngx_http_output_filter(r, u->out_bufs);\n\n        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n#if defined(nginx_version) && nginx_version >= 1001004\n        ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,\n                                &u->out_bufs, u->output.tag);\n#else\n        ngx_chain_update_chains(&u->free_bufs, &u->busy_bufs, &u->out_bufs,\n                                u->output.tag);\n#endif\n\n        dp->last_out = &u->out_bufs;\n    }\n\n    /* impossible to reach here */\n}\n\n\nngx_int_t\nngx_http_drizzle_output_col(ngx_http_request_t *r, drizzle_column_st *col)\n{\n    u_char                              *pos, *last;\n    drizzle_column_type_t                col_type = 0;\n    uint16_t                             std_col_type = 0;\n    const char                          *col_name = NULL;\n    uint16_t                             col_name_len = 0;\n    size_t                               size;\n\n    ngx_http_upstream_drizzle_peer_data_t   *dp = ngx_http_drizzle_get_peer_data(r);\n\n    if (col == NULL) {\n        return NGX_ERROR;\n    }\n\n    col_type = drizzle_column_type(col);\n    col_name = drizzle_column_name(col);\n    col_name_len = (uint16_t) strlen(col_name);\n\n    size = sizeof(uint16_t)     /* std col type */\n           + sizeof(uint16_t)     /* driver-specific col type */\n           + sizeof(uint16_t)     /* col name str len */\n           + col_name_len;        /* col name str len */\n\n    pos = ngx_http_drizzle_request_mem(r, dp, size);\n    if (pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = pos;\n\n    /* std column type */\n\n    std_col_type = (uint16_t) ngx_http_drizzle_std_col_type(col_type);\n\n#if 0\n    dd(\"std col type for %s: %d, %d (%d, %d, %d)\",\n       col_name, std_col_type, rds_col_type_blob,\n       rds_rough_col_type_str,\n       rds_rough_col_type_str << 14,\n       (uint16_t) (19 | (rds_rough_col_type_str << 14)));\n#endif\n\n    *(uint16_t *) last = std_col_type;\n    last += sizeof(uint16_t);\n\n    /* drizzle column type */\n    *(uint16_t *) last = col_type;\n    last += sizeof(uint16_t);\n\n    /* column name string length */\n    *(uint16_t *) last = col_name_len;\n    last += sizeof(uint16_t);\n\n    /* column name string data */\n    last = ngx_copy(last, col_name, col_name_len);\n\n    if ((size_t) (last - pos) != size) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"drizzle: FATAL: output column buffer error\");\n        return NGX_ERROR;\n    }\n\n    return ngx_http_drizzle_submit_mem(r, dp, size);\n}\n\n\nngx_int_t\nngx_http_drizzle_output_row(ngx_http_request_t *r, uint64_t row)\n{\n    u_char                              *pos, *last;\n    size_t                               size;\n\n    ngx_http_upstream_drizzle_peer_data_t   *dp = ngx_http_drizzle_get_peer_data(r);\n\n    size = sizeof(uint8_t);\n\n    pos = ngx_http_drizzle_request_mem(r, dp, size);\n    if (pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = pos;\n    *last++ = (row != 0);\n\n    if (row == 0) {\n        dp->seen_stream_end = 1;\n    }\n\n    return ngx_http_drizzle_submit_mem(r, dp, size);\n}\n\n\nngx_int_t\nngx_http_drizzle_output_field(ngx_http_request_t *r, size_t offset,\n    size_t len, size_t total, drizzle_field_t field)\n{\n    u_char                              *pos, *last;\n    size_t                               size = 0;\n\n    ngx_http_upstream_drizzle_peer_data_t   *dp = ngx_http_drizzle_get_peer_data(r);\n\n    if (offset == 0) {\n\n        if (len == 0 && total != 0) {\n            return NGX_DONE;\n        }\n\n        size = sizeof(uint32_t);     /* field total length */\n    }\n\n    /* (more) field data */\n    size += (uint32_t) len;\n\n    /* request memory */\n\n    pos = ngx_http_drizzle_request_mem(r, dp, size);\n    if (pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = pos;\n\n    /* fill in the buffer */\n\n    if (offset == 0) {\n        /* field total length */\n        if (field == NULL) {\n            *(uint32_t *) last = (uint32_t) -1;\n\n        } else {\n            *(uint32_t *) last = (uint32_t) total;\n        }\n\n        last += sizeof(uint32_t);\n    }\n\n    /* field data */\n    if (len && field) {\n        last = ngx_copy(last, field, (uint32_t) len);\n    }\n\n    if ((size_t) (last - pos) != size) {\n        dd(\"offset %d, len %d, size %d\", (int) offset,\n           (int) len, (int) size);\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"drizzle: FATAL: output field buffer error\");\n        return NGX_ERROR;\n    }\n\n    return ngx_http_drizzle_submit_mem(r, dp, size);\n}\n\n\nstatic rds_col_type_t\nngx_http_drizzle_std_col_type(drizzle_column_type_t col_type)\n{\n    dd(\"drizzle col type: %d\", col_type);\n\n    switch (col_type) {\n    case DRIZZLE_COLUMN_TYPE_DECIMAL:\n        return rds_col_type_decimal;\n\n    case DRIZZLE_COLUMN_TYPE_TINY:\n        return rds_col_type_smallint;\n\n    case DRIZZLE_COLUMN_TYPE_SHORT:\n        return rds_col_type_smallint;\n\n    case DRIZZLE_COLUMN_TYPE_LONG:\n        return rds_col_type_integer;\n\n    case DRIZZLE_COLUMN_TYPE_FLOAT:\n        return rds_col_type_real;\n\n    case DRIZZLE_COLUMN_TYPE_DOUBLE:\n        return rds_col_type_double;\n\n    case DRIZZLE_COLUMN_TYPE_NULL:\n        return rds_col_type_unknown;\n\n    case DRIZZLE_COLUMN_TYPE_TIMESTAMP:\n        return rds_col_type_timestamp;\n\n    case DRIZZLE_COLUMN_TYPE_LONGLONG:\n        return rds_col_type_bigint;\n\n    case DRIZZLE_COLUMN_TYPE_INT24:\n        return rds_col_type_integer;\n\n    case DRIZZLE_COLUMN_TYPE_DATE:\n        return rds_col_type_timestamp;\n\n    case DRIZZLE_COLUMN_TYPE_TIME:\n        return rds_col_type_time;\n\n    case DRIZZLE_COLUMN_TYPE_DATETIME:\n        return rds_col_type_timestamp;\n\n    case DRIZZLE_COLUMN_TYPE_YEAR:\n        return rds_col_type_smallint;\n\n    case DRIZZLE_COLUMN_TYPE_NEWDATE:\n        return rds_col_type_timestamp;\n\n    case DRIZZLE_COLUMN_TYPE_VARCHAR:\n        return rds_col_type_varchar;\n\n    case DRIZZLE_COLUMN_TYPE_BIT:\n        return rds_col_type_bit;\n\n    case DRIZZLE_COLUMN_TYPE_NEWDECIMAL:\n        return rds_col_type_decimal;\n\n    case DRIZZLE_COLUMN_TYPE_ENUM:\n        return rds_col_type_varchar;\n\n    case DRIZZLE_COLUMN_TYPE_SET:\n        return rds_col_type_varchar;\n\n    case DRIZZLE_COLUMN_TYPE_TINY_BLOB:\n        return rds_col_type_blob;\n\n    case DRIZZLE_COLUMN_TYPE_MEDIUM_BLOB:\n        return rds_col_type_blob;\n\n    case DRIZZLE_COLUMN_TYPE_LONG_BLOB:\n        return rds_col_type_blob;\n\n    case DRIZZLE_COLUMN_TYPE_BLOB:\n        return rds_col_type_blob;\n\n    case DRIZZLE_COLUMN_TYPE_VAR_STRING:\n        return rds_col_type_varchar;\n\n    case DRIZZLE_COLUMN_TYPE_STRING:\n        return rds_col_type_varchar;\n\n    case DRIZZLE_COLUMN_TYPE_GEOMETRY:\n        return rds_col_type_varchar;\n\n    default:\n        return rds_col_type_unknown;\n    }\n\n    /* impossible to reach here */\n}\n\n\nstatic u_char *\nngx_http_drizzle_request_mem(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len)\n{\n    ngx_int_t                rc;\n    u_char                  *p;\n\n    rc = ngx_http_drizzle_get_buf(r, dp);\n\n    if (rc != NGX_OK) {\n        return NULL;\n    }\n\n    if (dp->avail_out < len) {\n        p = ngx_http_drizzle_get_postponed(r, dp, len);\n        if (p == NULL) {\n            return NULL;\n        }\n\n        dp->postponed.pos = p;\n        dp->postponed.last = p + len;\n\n        return p;\n    }\n\n    return dp->out_buf->last;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_get_buf(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp)\n{\n    ngx_http_drizzle_loc_conf_t         *conf = dp->loc_conf;\n    ngx_http_upstream_t                 *u = dp->upstream;\n\n    dd(\"MEM enter\");\n\n    if (dp->avail_out) {\n        return NGX_OK;\n    }\n\n    if (u->free_bufs) {\n        dd(\"MEM reusing temp buf from free_bufs\");\n\n        dp->out_buf = u->free_bufs->buf;\n        u->free_bufs = u->free_bufs->next;\n\n    } else {\n        dd(\"MEM creating temp buf with size: %d\", (int) conf->buf_size);\n        dp->out_buf = ngx_create_temp_buf(r->pool, conf->buf_size);\n\n        if (dp->out_buf == NULL) {\n            return NGX_ERROR;\n        }\n\n        dp->out_buf->tag = (ngx_buf_tag_t) &ngx_http_drizzle_module;\n        dp->out_buf->recycled = 1;\n    }\n\n    dp->avail_out = conf->buf_size;\n\n    return NGX_OK;\n}\n\n\nstatic u_char *\nngx_http_drizzle_get_postponed(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len)\n{\n    u_char          *p;\n\n    dd(\"MEM enter\");\n\n    if (dp->cached.start == NULL) {\n        goto alloc;\n    }\n\n    if ((size_t) (dp->cached.end - dp->cached.start) < len) {\n        ngx_pfree(r->pool, dp->cached.start);\n        goto alloc;\n    }\n\n    return dp->cached.start;\n\nalloc:\n\n    p = ngx_palloc(r->pool, len);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    dp->cached.start = p;\n    dp->cached.end = p + len;\n\n    return p;\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_submit_mem(ngx_http_request_t *r,\n    ngx_http_upstream_drizzle_peer_data_t *dp, size_t len)\n{\n    ngx_chain_t             *cl;\n    ngx_int_t                rc;\n    size_t                   postponed_len;\n\n    ngx_http_drizzle_loc_conf_t         *conf = dp->loc_conf;\n\n    if (dp->postponed.pos != NULL) {\n        dd(\"MEM copy postponed data over to u->out_bufs for len %d\", (int) len);\n\n        postponed_len = dp->postponed.last - dp->postponed.pos;\n\n        if (postponed_len > dp->avail_out) {\n            /* we should ensure that rds atoms do not get\n             * splitted into multiple bufs. */\n\n            if (dp->out_buf && dp->out_buf->pos != dp->out_buf->last) {\n                /* save the current dp->out_buf */\n                cl = ngx_alloc_chain_link(r->pool);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl->buf = dp->out_buf;\n                cl->next = NULL;\n                *dp->last_out = cl;\n                dp->last_out = &cl->next;\n            }\n\n            /* create a buf for the postponed buf */\n\n            len = postponed_len > conf->buf_size ?\n                postponed_len : conf->buf_size;\n\n            dp->out_buf = ngx_create_temp_buf(r->pool, len);\n\n            if (dp->out_buf == NULL) {\n                return NGX_ERROR;\n            }\n\n            dp->out_buf->tag = (ngx_buf_tag_t) &ngx_http_drizzle_module;\n            dp->out_buf->recycled = 1;\n\n            dp->out_buf->last = ngx_copy(dp->out_buf->last, dp->postponed.pos,\n                                         postponed_len);\n\n            dp->avail_out = len - postponed_len;\n\n            dp->postponed.pos = NULL;\n\n            if (dp->avail_out == 0) {\n                /* save the new big buf */\n\n                cl = ngx_alloc_chain_link(r->pool);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl->buf = dp->out_buf;\n                cl->next = NULL;\n                *dp->last_out = cl;\n                dp->last_out = &cl->next;\n            }\n\n            return NGX_OK;\n        }\n\n        for ( ;; ) {\n            len = dp->postponed.last - dp->postponed.pos;\n            if (len > dp->avail_out) {\n                len = dp->avail_out;\n            }\n\n            dp->out_buf->last = ngx_copy(dp->out_buf->last, dp->postponed.pos,\n                                         len);\n\n            dp->avail_out -= len;\n\n            dp->postponed.pos += len;\n\n            if (dp->postponed.pos == dp->postponed.last) {\n                dp->postponed.pos = NULL;\n            }\n\n            if (dp->avail_out > 0) {\n                break;\n            }\n\n            dd(\"MEM save dp->out_buf\");\n\n            cl = ngx_alloc_chain_link(r->pool);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf = dp->out_buf;\n            cl->next = NULL;\n            *dp->last_out = cl;\n            dp->last_out = &cl->next;\n\n            if (dp->postponed.pos == NULL) {\n                break;\n            }\n\n            rc = ngx_http_drizzle_get_buf(r, dp);\n            if (rc != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        return NGX_OK;\n    }\n\n    dd(\"MEM consuming out_buf for %d\", (int) len);\n\n    dp->out_buf->last += len;\n    dp->avail_out -= len;\n\n    if (dp->avail_out == 0) {\n        dd(\"MEM save dp->out_buf\");\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = dp->out_buf;\n        cl->next = NULL;\n        *dp->last_out = cl;\n        dp->last_out = &cl->next;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_output.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_OUTPUT_H\n#define NGX_HTTP_DRIZZLE_OUTPUT_H\n\n#include \"ngx_http_drizzle_upstream.h\"\n\nngx_int_t ngx_http_drizzle_output_result_header(ngx_http_request_t *r,\n        drizzle_result_st *res);\n\nngx_int_t ngx_http_drizzle_output_col(ngx_http_request_t *r,\n        drizzle_column_st *res);\n\nngx_int_t ngx_http_drizzle_output_row(ngx_http_request_t *r, uint64_t row);\n\nngx_int_t ngx_http_drizzle_output_field(ngx_http_request_t *r, size_t offset,\n        size_t len, size_t total, drizzle_field_t field);\n\nngx_int_t ngx_http_drizzle_output_bufs(ngx_http_request_t *r,\n        ngx_http_upstream_drizzle_peer_data_t *dp);\n\n#endif /* NGX_HTTP_DRIZZLE_OUTPUT_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_processor.c",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_processor.h\"\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_util.h\"\n#include \"ngx_http_drizzle_output.h\"\n#include \"ngx_http_drizzle_upstream.h\"\n\n\n#define MYSQL_ER_NO_SUCH_TABLE 1146\n\nstatic ngx_int_t ngx_http_upstream_drizzle_connect(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc);\nstatic ngx_int_t ngx_http_upstream_drizzle_send_query(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc);\nstatic ngx_int_t ngx_http_upstream_drizzle_recv_cols(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc);\nstatic ngx_int_t ngx_http_upstream_drizzle_recv_rows(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc);\n\n\nngx_int_t\nngx_http_drizzle_process_events(ngx_http_request_t *r)\n{\n    ngx_http_upstream_t                         *u;\n    ngx_connection_t                            *c;\n    ngx_http_upstream_drizzle_peer_data_t       *dp;\n    drizzle_con_st                              *dc;\n    ngx_int_t                                    rc;\n#if 0\n    drizzle_return_t                             ret;\n#endif\n\n    u = r->upstream;\n    c = u->peer.connection;\n\n    dp = ngx_http_drizzle_get_peer_data(r);\n\n    if (dp == NULL) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"process events: it seems you \"\n                      \"are using a non-drizzle upstream backend\"\n        );\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"drizzle process events, state: %d\", dp->state);\n\n    dc = dp->drizzle_con;\n\n    switch (dp->state) {\n    case state_db_connect:\n        rc = ngx_http_upstream_drizzle_connect(r, c, dp, dc);\n        break;\n\n    case state_db_idle: /* from connection pool */\n        c->log->action = \"sending query to drizzle upstream\";\n        /* fallthrough */\n\n    case state_db_send_query:\n        rc = ngx_http_upstream_drizzle_send_query(r, c, dp, dc);\n        break;\n\n    case state_db_recv_cols:\n        rc = ngx_http_upstream_drizzle_recv_cols(r, c, dp, dc);\n        break;\n\n    case state_db_recv_rows:\n        rc = ngx_http_upstream_drizzle_recv_rows(r, c, dp, dc);\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"unknown state: %d\", (int) dp->state);\n        return NGX_ERROR;\n    }\n\n    dd(\"rc == %d\", (int) rc);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n\n        return NGX_ERROR;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        ngx_http_upstream_drizzle_finalize_request(r, u, rc);\n\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_AGAIN) {\n#if 0\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n#endif\n\n        rc = ngx_http_drizzle_output_bufs(r, dp);\n\n        if (rc == NGX_ERROR || rc > NGX_OK) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n            return NGX_ERROR;\n        }\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_connect(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc)\n{\n    drizzle_return_t             ret;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"drizzle connect: user %s, password %s\", dc->user,\n                   dc->password);\n\n    ret = drizzle_con_connect(dc);\n\n    if (ret == DRIZZLE_RETURN_IO_WAIT) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"drizzle libdrizzle returned IO_WAIT while \"\n                       \"connecting\");\n\n#if 0\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n#endif\n\n        return NGX_AGAIN;\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (ret != DRIZZLE_RETURN_OK) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to connect: %d: %s\",\n                      (int) ret, drizzle_error(dc->drizzle));\n\n        return NGX_ERROR;\n    }\n\n    /* ret == DRIZZLE_RETURN_OK */\n\n    c->log->action = \"sending query to drizzle upstream\";\n\n    return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_send_query(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc)\n{\n    ngx_http_upstream_t         *u = r->upstream;\n    drizzle_return_t             ret;\n    ngx_int_t                    rc;\n    ngx_str_t                    query;\n    ngx_flag_t                   has_set_names = 0;\n    ngx_flag_t                   enable_charset = 0;\n\n    dd(\"enable charset: %d\", (int) dp->enable_charset);\n\n    if (dp->enable_charset && ! dp->has_set_names) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"drizzle enables connection charset setting\");\n\n        query.len = dp->set_names_query->len;\n        query.data = dp->set_names_query->data;\n\n    } else {\n        query.data = dp->query.data;\n        query.len = dp->query.len;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"drizzle sending query \\\"%V\\\"\", &query);\n\n    (void) drizzle_query(dc, &dp->drizzle_res, (const char *) query.data,\n                         query.len, &ret);\n\n    if (ret == DRIZZLE_RETURN_IO_WAIT) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"drizzle libdrizzle returned IO_WAIT while sending \"\n                       \"query\");\n\n        if (dp->state != state_db_send_query) {\n            dp->state = state_db_send_query;\n\n            if (c->write->timer_set) {\n                ngx_del_timer(c->write);\n            }\n\n            ngx_add_timer(c->write, u->conf->send_timeout);\n        }\n\n        return NGX_AGAIN;\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (ret != DRIZZLE_RETURN_OK) {\n#if 1\n        if (ret == DRIZZLE_RETURN_ERROR_CODE) {\n            if (drizzle_error_code(dc->drizzle) == MYSQL_ER_NO_SUCH_TABLE) {\n                ngx_log_error(NGX_LOG_NOTICE, c->log, 0,\n                              \"failed to send query: %i (%d): %s\",\n                              ret, drizzle_error_code(dc->drizzle),\n                              drizzle_error(dc->drizzle));\n\n                if (dp->enable_charset && ! dp->has_set_names) {\n                    c->log->action = \"sending query to drizzle upstream\";\n                    dp->has_set_names = 1;\n\n                    return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);\n                }\n\n                ngx_http_upstream_drizzle_done(r, u, dp, NGX_HTTP_GONE);\n\n                return NGX_DONE;\n            }\n        }\n#endif\n\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"failed to send query: %d (%d): %s\",\n                      (int) ret, drizzle_error_code(dc->drizzle),\n                      drizzle_error(dc->drizzle));\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* ret == DRIZZLE_RETURN_OK */\n\n    dd_drizzle_result(&dp->drizzle_res);\n\n    dd(\"after drizzle restult\");\n\n    if (dp->enable_charset) {\n        enable_charset = 1;\n    }\n\n    has_set_names = dp->has_set_names;\n\n    rc = ngx_http_drizzle_output_result_header(r, &dp->drizzle_res);\n\n    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    if (rc == NGX_DONE) {\n        if (enable_charset && ! has_set_names) {\n            c->log->action = \"sending query to drizzle upstream\";\n            dp->has_set_names = 1;\n\n            dp->state = state_db_idle;\n\n            return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);\n        }\n\n        /* no data set following the header */\n        return rc;\n    }\n\n    c->log->action = \"receiving result set columns from drizzle upstream\";\n\n    return ngx_http_upstream_drizzle_recv_cols(r, c, dp, dc);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_recv_cols(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc)\n{\n    drizzle_column_st               *col;\n    ngx_int_t                        rc;\n    drizzle_return_t                 ret;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"drizzle receive resultset columns\");\n\n    for (;;) {\n        col = drizzle_column_read(&dp->drizzle_res, &dp->drizzle_col, &ret);\n\n        if (ret == DRIZZLE_RETURN_IO_WAIT) {\n\n            if (dp->state != state_db_recv_cols) {\n                dp->state = state_db_recv_cols;\n\n                if (c->read->timer_set) {\n                    ngx_del_timer(c->read);\n                }\n\n                ngx_add_timer(c->read, dp->loc_conf->recv_cols_timeout);\n\n            }\n\n#if 0\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n#endif\n\n            return NGX_AGAIN;\n        }\n\n        if (ret != DRIZZLE_RETURN_OK) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"failed to recv cols: %d: %s\",\n                          (int) ret,\n                          drizzle_error(dc->drizzle));\n\n            return NGX_ERROR;\n        }\n\n        /* ret == DRIZZLE_RETURN_OK */\n\n        if (col) {\n            rc = ngx_http_drizzle_output_col(r, col);\n\n            drizzle_column_free(col);\n\n            if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                return rc;\n            }\n\n        } else { /* after the last column */\n            if (c->read->timer_set) {\n                ngx_del_timer(c->read);\n            }\n\n            c->log->action = \"receiving result set rows from drizzle \"\n                             \"upstream\";\n\n            return ngx_http_upstream_drizzle_recv_rows(r, c, dp, dc);\n        }\n\n        dd_drizzle_column(col);\n    }\n\n    /* impossible to reach here */\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_recv_rows(ngx_http_request_t *r,\n    ngx_connection_t *c, ngx_http_upstream_drizzle_peer_data_t *dp,\n    drizzle_con_st *dc)\n{\n    ngx_http_upstream_t             *u = r->upstream;\n    ngx_int_t                        rc;\n    drizzle_return_t                 ret;\n    size_t                           offset;\n    size_t                           len;\n    size_t                           total;\n    drizzle_field_t                  field;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"drizzle receive resultset rows\");\n\n    for (;;) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"drizzle receive resultset row %uL\", dp->drizzle_row);\n\n        if (dp->drizzle_row == 0) {\n            dp->drizzle_row = drizzle_row_read(&dp->drizzle_res, &ret);\n\n            if (ret == DRIZZLE_RETURN_IO_WAIT) {\n                dp->drizzle_row = 0;\n\n                goto io_wait;\n            }\n\n            if (ret != DRIZZLE_RETURN_OK) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"failed to read row: %d: %s\",\n                              (int) ret,\n                              drizzle_error(dc->drizzle));\n\n                return NGX_ERROR;\n            }\n\n            /* ret == DRIZZLE_RETURN_OK */\n\n            rc = ngx_http_drizzle_output_row(r, dp->drizzle_row);\n\n            if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                drizzle_result_free(&dp->drizzle_res);\n\n                return rc;\n            }\n\n            if (dp->drizzle_row == 0) {\n                /* after last row */\n\n                drizzle_result_free(&dp->drizzle_res);\n\n                if (c->read->timer_set) {\n                    ngx_del_timer(c->read);\n                }\n\n                if (dp->enable_charset && ! dp->has_set_names) {\n                    c->log->action = \"sending query to drizzle upstream\";\n                    dp->has_set_names = 1;\n\n                    return ngx_http_upstream_drizzle_send_query(r, c, dp, dc);\n                }\n\n                ngx_http_upstream_drizzle_done(r, u, dp, NGX_DONE);\n                return NGX_DONE;\n            }\n        }\n\n        /* dp->drizzle_row != 0 */\n\n        for (;;) {\n            field = drizzle_field_read(&dp->drizzle_res, &offset, &len,\n                                       &total, &ret);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"drizzle field read: %p (offset %z, len %z)\",\n                           field, offset, len);\n\n            if (ret == DRIZZLE_RETURN_IO_WAIT) {\n                goto io_wait;\n            }\n\n            if (ret == DRIZZLE_RETURN_ROW_END) {\n                /* reached the end of the current row */\n                break;\n            }\n\n            if (ret != DRIZZLE_RETURN_OK) {\n                drizzle_result_free(&dp->drizzle_res);\n\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"failed to read row field: %d: %s\",\n                              (int) ret,\n                              drizzle_error(dc->drizzle));\n\n                return NGX_ERROR;\n            }\n\n            /* ret == DRIZZLE_RETURN_OK */\n\n            rc = ngx_http_drizzle_output_field(r, offset, len, total, field);\n\n            if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                drizzle_result_free(&dp->drizzle_res);\n\n                return rc;\n            }\n\n            if (field) {\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                               \"drizzle field value read: %*s\", len, field);\n            }\n        }\n\n        dp->drizzle_row = 0;\n    }\n\n    /* impossible to reach here */\n\nio_wait:\n\n    if (dp->state != state_db_recv_rows) {\n        dp->state = state_db_recv_rows;\n\n        if (c->read->timer_set) {\n            ngx_del_timer(c->read);\n        }\n\n        ngx_add_timer(c->read, dp->loc_conf->recv_rows_timeout);\n    }\n\n#if 0\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_AGAIN;\n}\n\n\nvoid\nngx_http_upstream_drizzle_done(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_http_upstream_drizzle_peer_data_t *dp,\n    ngx_int_t rc)\n{\n    ngx_connection_t            *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"drizzle upstream done\");\n\n    (void) ngx_http_drizzle_output_bufs(r, dp);\n\n    /* to persuade Maxim Dounin's ngx_http_upstream_keepalive\n     * module to cache the current connection */\n\n    u->length = 0;\n\n    if (rc == NGX_DONE) {\n        u->header_sent = 1;\n        u->headers_in.status_n = NGX_HTTP_OK;\n        rc = NGX_OK;\n\n    } else {\n        r->headers_out.status = rc;\n        u->headers_in.status_n = rc;\n    }\n\n    c = u->peer.connection;\n\n    c->log->action = \"being idle\";\n\n    /* reset the state machine */\n    dp->state = state_db_idle;\n\n    dd(\"about to finalize request...\");\n    ngx_http_upstream_drizzle_finalize_request(r, u, rc);\n    dd(\"after finalize request...\");\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_processor.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_PROCESSOR_H\n#define NGX_HTTP_DRIZZLE_PROCESSOR_H\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_upstream.h\"\n#include <ngx_http.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_http_drizzle_process_events(ngx_http_request_t *r);\n\nvoid ngx_http_upstream_drizzle_done(ngx_http_request_t *r,\n        ngx_http_upstream_t *u, ngx_http_upstream_drizzle_peer_data_t *dp,\n        ngx_int_t rc);\n\n#endif /* NGX_HTTP_DRIZZLE_PROCESSOR_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_quoting.c",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_quoting.h\"\n#include \"ngx_http_drizzle_checker.h\"\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n/* static */ ngx_http_drizzle_var_type_t  ngx_http_drizzle_builtin_types[] = {\n    { ngx_string(\"string\"),\n      NULL,\n      NULL,\n      quotes_type_single\n    },\n    { ngx_string(\"int\"),\n      ngx_http_drizzle_check_int,\n      NULL,\n      quotes_type_none\n    },\n    { ngx_string(\"bool\"),\n      ngx_http_drizzle_check_bool,\n      NULL,\n      quotes_type_none\n    },\n    { ngx_string(\"float\"),\n      ngx_http_drizzle_check_float,\n      NULL,\n      quotes_type_none\n    },\n    { ngx_string(\"column\"),\n      ngx_http_drizzle_check_col,\n      NULL,\n      quotes_type_none\n    },\n    { ngx_string(\"table\"),\n      ngx_http_drizzle_check_table,\n      NULL,\n      quotes_type_none\n    },\n    { ngx_string(\"keyword\"),\n      ngx_http_drizzle_check_keyword,\n      NULL,\n      quotes_type_none\n    }\n};\n"
  },
  {
    "path": "src/ngx_http_drizzle_quoting.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_QUOTING_H\n#define NGX_HTTP_DRIZZLE_QUOTING_H\n\n#include \"ngx_http_drizzle_module.h\"\n\ntypedef ngx_int_t (* ngx_http_drizzle_type_checker_pt) (ngx_str_t *value,\n        void *data);\n\ntypedef enum {\n    quotes_type_none,\n    quotes_type_single,\n    quotes_type_double,\n    quotes_type_back\n\n} ngx_http_drizzle_quotes_type_t;\n\n\ntypedef struct {\n    ngx_str_t                               name;\n    ngx_http_drizzle_type_checker_pt        checker;\n    void                                   *checker_data;\n    ngx_http_drizzle_quotes_type_t          quotes;\n\n} ngx_http_drizzle_var_type_t;\n\n\ntypedef struct {\n    ngx_int_t                            src_var;\n    ngx_int_t                            dest_var;\n    ngx_http_drizzle_var_type_t         *type;\n    ngx_str_t                            errstr;\n\n} ngx_http_drizzle_var_to_quote_t;\n\n#endif /* NGX_HTTP_DRIZZLE_QUOTING_H */\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_upstream.c",
    "content": "/* Copyright (C) agentzh */\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_upstream.h\"\n#include \"ngx_http_drizzle_keepalive.h\"\n#include \"ngx_http_drizzle_processor.h\"\n#include \"ngx_http_drizzle_util.h\"\n\nenum {\n    ngx_http_drizzle_default_port = 3306\n};\n\nstatic void ngx_http_upstream_drizzle_cleanup(void *data);\n\nstatic ngx_int_t ngx_http_upstream_drizzle_init(ngx_conf_t *cf,\n        ngx_http_upstream_srv_conf_t *uscf);\n\nstatic ngx_int_t ngx_http_upstream_drizzle_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *uscf);\n\nstatic ngx_int_t ngx_http_upstream_drizzle_get_peer(ngx_peer_connection_t *pc,\n        void *data);\n\nstatic void ngx_http_upstream_drizzle_free_peer(ngx_peer_connection_t *pc,\n        void *data, ngx_uint_t state);\n\n/* just a work-around to override the default u->output_filter */\nstatic ngx_int_t ngx_http_drizzle_output_filter(void *data, ngx_chain_t *in);\n\n\nvoid *\nngx_http_upstream_drizzle_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_pool_cleanup_t                      *cln;\n    ngx_http_upstream_drizzle_srv_conf_t    *conf;\n\n    dd(\"drizzle create srv conf\");\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_http_upstream_drizzle_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /* set by ngx_pcalloc:\n     *      conf->peers   = NULL\n     *      conf->current = 0\n     *      conf->servers = NULL\n     *      conf->single = 0\n     *      conf->max_cached = 0\n     *      conf->overflow = 0 (drizzle_keepalive_overflow_ignore)\n     */\n\n    conf->pool = cf->pool;\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    (void) drizzle_create(&conf->drizzle);\n\n    cln->handler = ngx_http_upstream_drizzle_cleanup;\n    cln->data = &conf->drizzle;\n\n    drizzle_add_options(&conf->drizzle, DRIZZLE_NON_BLOCKING);\n\n    /* we use 0 timeout for the underlying poll event model\n     * used by libdrizzle itself. */\n    drizzle_set_timeout(&conf->drizzle, 0);\n\n    return conf;\n}\n\n\n/* mostly based on ngx_http_upstream_server in\n * ngx_http_upstream.c of nginx 0.8.30.\n * Copyright (C) Igor Sysoev */\nchar *\nngx_http_upstream_drizzle_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_upstream_drizzle_srv_conf_t        *dscf = conf;\n    ngx_http_upstream_drizzle_server_t          *ds;\n    ngx_str_t                                   *value;\n    ngx_url_t                                    u;\n    ngx_uint_t                                   i, j;\n    ngx_http_upstream_srv_conf_t                *uscf;\n    ngx_str_t                                    protocol;\n    ngx_str_t                                    charset;\n    u_char                                      *p;\n    size_t                                       len;\n\n    dd(\"entered drizzle_server directive handler...\");\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (dscf->servers == NULL) {\n        dscf->servers = ngx_array_create(cf->pool, 4,\n                                 sizeof(ngx_http_upstream_drizzle_server_t));\n\n        if (dscf->servers == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        uscf->servers = dscf->servers;\n    }\n\n    ds = ngx_array_push(dscf->servers);\n    if (ds == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(ds, sizeof(ngx_http_upstream_drizzle_server_t));\n\n    value = cf->args->elts;\n\n    /* parse the first name:port argument */\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.default_port = ngx_http_drizzle_default_port;\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"drizzle: %s in upstream \\\"%V\\\"\", u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    ds->addrs  = u.addrs;\n    ds->naddrs = u.naddrs;\n    ds->port   = u.port;\n    ds->protocol = ngx_http_drizzle_protocol;\n\n    /* parse various options */\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"dbname=\", sizeof(\"dbname=\") - 1)\n            == 0)\n        {\n            ds->dbname.len = value[i].len - (sizeof(\"dbname=\") - 1);\n\n            if (ds->dbname.len >= DRIZZLE_MAX_DB_SIZE) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                       \"drizzle: \\\"dbname\\\" value too large in upstream \\\"%V\\\"\"\n                       \" (at most %d bytes)\",\n                       dscf->peers->name,\n                       (int) DRIZZLE_MAX_DB_SIZE);\n\n                return NGX_CONF_ERROR;\n            }\n\n            ds->dbname.data = &value[i].data[sizeof(\"dbname=\") - 1];\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"user=\", sizeof(\"user=\") - 1)\n            == 0)\n        {\n            ds->user.len = value[i].len - (sizeof(\"user=\") - 1);\n\n            if (ds->user.len >= DRIZZLE_MAX_USER_SIZE) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                       \"drizzle: \\\"user\\\" value too large in upstream \\\"%V\\\"\"\n                       \" (at most %d bytes)\",\n                       dscf->peers->name,\n                       (int) DRIZZLE_MAX_USER_SIZE);\n\n                return NGX_CONF_ERROR;\n            }\n\n            ds->user.data = &value[i].data[sizeof(\"user=\") - 1];\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"password=\", sizeof(\"password=\") - 1)\n            == 0)\n        {\n            ds->password.len = value[i].len - (sizeof(\"password=\") - 1);\n\n            if (ds->password.len >= DRIZZLE_MAX_PASSWORD_SIZE) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                       \"drizzle: \\\"password\\\" value too large in upstream \"\n                       \"\\\"%V\\\" (at most %d bytes)\",\n                       dscf->peers->name,\n                       (int) DRIZZLE_MAX_PASSWORD_SIZE);\n\n                return NGX_CONF_ERROR;\n            }\n\n            ds->password.data = &value[i].data[sizeof(\"password=\") - 1];\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"protocol=\", sizeof(\"protocol=\") - 1)\n            == 0)\n        {\n            protocol.len = value[i].len - (sizeof(\"protocol=\") - 1);\n            protocol.data = &value[i].data[sizeof(\"protocol=\") - 1];\n\n            switch (protocol.len) {\n\n            case 5:\n                if (ngx_http_drizzle_strcmp_const(protocol.data, \"mysql\") == 0)\n                {\n                    ds->protocol = ngx_http_mysql_protocol;\n                } else {\n                    continue;\n                }\n\n                break;\n\n            case 7:\n                if (ngx_http_drizzle_strcmp_const(protocol.data,\n                            \"drizzle\") != 0)\n                {\n                    continue;\n                }\n\n                break;\n\n            default:\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"drizzle: invalid protocol \\\"%V\\\"\"\n                                   \" in drizzle_server\", &protocol);\n\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"charset=\", sizeof(\"charset=\") - 1)\n            == 0)\n        {\n            charset.len = value[i].len - (sizeof(\"charset=\") - 1);\n            charset.data = &value[i].data[sizeof(\"charset=\") - 1];\n\n            dd(\"charset: %.*s\", (int) charset.len, charset.data);\n\n            if (charset.len == 0) {\n                continue;\n            }\n\n            for (j = 0; j < charset.len; j++) {\n                if (charset.data[j] == '\\'') {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"bad charste value \\\"%V\\\" in\"\n                                       \" drizzle_server\", &charset);\n\n                    return NGX_CONF_ERROR;\n                }\n            }\n\n            len = sizeof(\"set names ''\") - 1 + charset.len;\n\n            p = ngx_palloc(cf->pool, len);\n            if (p == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ds->set_names_query.data = p;\n            ds->set_names_query.len = len;\n\n            dd(\"charset query len: %d\", (int) len);\n\n            p = ngx_copy(p, \"set names '\", sizeof(\"set names '\") - 1);\n            p = ngx_copy(p, charset.data, charset.len);\n            *p = '\\'';\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\" in\"\n                           \" drizzle_server\", &value[i]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    dd(\"reset init_upstream...\");\n\n    uscf->peer.init_upstream = ngx_http_upstream_drizzle_init;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_init(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *uscf)\n{\n    ngx_uint_t                               i, j, n;\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n    ngx_http_upstream_drizzle_server_t      *server;\n    ngx_http_upstream_drizzle_peers_t       *peers;\n    size_t                                   len;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"drizzle upstream init\");\n\n    uscf->peer.init = ngx_http_upstream_drizzle_init_peer;\n\n    dscf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_drizzle_module);\n\n    if (dscf->servers == NULL || dscf->servers->nelts == 0) {\n        /* XXX an upstream implicitly defined by drizzle_pass, etc.,\n         * is not allowed for now */\n\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"drizzle: no drizzle_server defined in upstream \\\"%V\\\"\"\n                      \" in %s:%ui\",\n                      &uscf->host, uscf->file_name, uscf->line);\n\n        return NGX_ERROR;\n    }\n\n    /* dscf->servers != NULL */\n\n    server = uscf->servers->elts;\n\n    n = 0;\n\n    for (i = 0; i < uscf->servers->nelts; i++) {\n        n += server[i].naddrs;\n    }\n\n    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_drizzle_peers_t)\n                        + sizeof(ngx_http_upstream_drizzle_peer_t) * (n - 1));\n\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers->single = (n == 1);\n    peers->number = n;\n    peers->name = &uscf->host;\n\n    n = 0;\n\n    for (i = 0; i < uscf->servers->nelts; i++) {\n        for (j = 0; j < server[i].naddrs; j++) {\n            peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;\n            peers->peer[n].socklen = server[i].addrs[j].socklen;\n            peers->peer[n].name = server[i].addrs[j].name;\n            peers->peer[n].port = server[i].port;\n            peers->peer[n].user = server[i].user;\n            peers->peer[n].password = server[i].password;\n            peers->peer[n].dbname = server[i].dbname;\n            peers->peer[n].protocol = server[i].protocol;\n            peers->peer[n].set_names_query = &server[i].set_names_query;\n\n            len = NGX_SOCKADDR_STRLEN + 1 /* for '\\0' */;\n\n            peers->peer[n].host = ngx_palloc(cf->pool, len);\n\n            if (peers->peer[n].host == NULL) {\n                return NGX_ERROR;\n            }\n\n            len = ngx_sock_ntop(peers->peer[n].sockaddr,\n#if defined(nginx_version) && (nginx_version >= 1005003)\n                                peers->peer[n].socklen,\n#endif\n                                peers->peer[n].host, len - 1, 0 /* no port */);\n\n            peers->peer[n].host[len] = '\\0';\n\n            n++;\n        }\n    }\n\n    dscf->peers = peers;\n\n    dscf->active_conns = 0;\n\n    if (dscf->max_cached) {\n        return ngx_http_drizzle_keepalive_init(cf->pool, dscf);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *uscf)\n{\n    ngx_http_upstream_drizzle_peer_data_t   *dp;\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n    ngx_http_upstream_t                     *u;\n    ngx_http_core_loc_conf_t                *clcf;\n    ngx_http_drizzle_loc_conf_t             *dlcf;\n    ngx_http_drizzle_ctx_t                  *dctx;\n    ngx_drizzle_mixed_t                     *query;\n    ngx_str_t                                dbname, sql;\n    ngx_uint_t                               i;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"drizzle init peer\");\n\n    dp = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_drizzle_peer_data_t));\n    if (dp == NULL) {\n        goto failed;\n    }\n\n    u = r->upstream;\n\n    dp->upstream = u;\n    dp->request  = r;\n\n    dp->last_out = &u->out_bufs;\n\n    dscf = ngx_http_conf_upstream_srv_conf(uscf, ngx_http_drizzle_module);\n\n    dp->srv_conf = dscf;\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_drizzle_module);\n\n    dp->loc_conf = dlcf;\n\n    dp->query.len  = 0;\n    dp->dbname.len = 0;\n\n    /* to force ngx_output_chain not to use ngx_chain_writer */\n\n    u->output.output_filter = ngx_http_drizzle_output_filter;\n    u->output.filter_ctx = r;\n    u->output.in   = NULL;\n    u->output.busy = NULL;\n\n    u->peer.data = dp;\n    u->peer.get = ngx_http_upstream_drizzle_get_peer;\n    u->peer.free = ngx_http_upstream_drizzle_free_peer;\n\n    /* Stash dp in the module ctx so handlers can recover it even if\n     * another module (e.g. the standard upstream keepalive, which is\n     * implicit in nginx 1.29.7+) wraps u->peer.data afterwards. */\n    dctx = ngx_http_get_module_ctx(r, ngx_http_drizzle_module);\n    if (dctx != NULL) {\n        dctx->peer_data = dp;\n    }\n\n    /* prepare dbname */\n\n    dp->dbname.len = 0;\n\n    if (dlcf->dbname) {\n        /* check if dbname requires overriding at request time */\n        if (ngx_http_complex_value(r, dlcf->dbname, &dbname) != NGX_OK) {\n            goto failed;\n        }\n\n        if (dbname.len) {\n            if (dbname.len >= DRIZZLE_MAX_DB_SIZE) {\n                ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0,\n                       \"drizzle: \\\"dbname\\\" value too large in upstream \\\"%V\\\"\",\n                       dscf->peers->name);\n\n                goto failed;\n            }\n\n            dp->dbname = dbname;\n        }\n    }\n\n    /* prepare SQL query */\n\n    if (dlcf->methods_set & r->method) {\n        /* method-specific query */\n        dd(\"using method-specific query\");\n\n        query = dlcf->queries->elts;\n        for (i = 0; i < dlcf->queries->nelts; i++) {\n            if (query[i].key & r->method) {\n                query = &query[i];\n                break;\n            }\n        }\n\n        if (i == dlcf->queries->nelts) {\n            goto failed;\n        }\n\n    } else {\n        /* default query */\n        dd(\"using default query\");\n\n        query = dlcf->default_query;\n    }\n\n    if (query->cv) {\n        /* complex value */\n        dd(\"using complex value\");\n\n        if (ngx_http_complex_value(r, query->cv, &sql) != NGX_OK) {\n            goto failed;\n        }\n\n        if (sql.len == 0) {\n            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"drizzle: empty \\\"drizzle_query\\\" (was: \\\"%V\\\")\"\n                          \" in location \\\"%V\\\"\", &query->cv->value,\n                          &clcf->name);\n\n            goto failed;\n        }\n\n        dp->query = sql;\n\n        return NGX_OK;\n    }\n\n    /* simple value */\n    dd(\"using simple value\");\n\n    dp->query = query->sv;\n\n    return NGX_OK;\n\nfailed:\n\n#if defined(nginx_version) && (nginx_version >= 8017)\n    return NGX_ERROR;\n#else\n    r->upstream->peer.data = NULL;\n\n    return NGX_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_drizzle_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_drizzle_peer_data_t   *dp = data;\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n    ngx_http_upstream_drizzle_peers_t       *peers;\n    ngx_http_upstream_drizzle_peer_t        *peer;\n#if defined(nginx_version) && (nginx_version < 8017)\n    ngx_http_drizzle_ctx_t                  *dctx;\n#endif\n    ngx_connection_t                        *c = NULL;\n    drizzle_con_st                          *dc = NULL;\n    ngx_str_t                                dbname;\n    drizzle_return_t                         ret;\n    ngx_socket_t                             fd;\n    ngx_event_t                             *rev, *wev;\n    ngx_int_t                                rc;\n    ngx_int_t                                event;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, dp->request->connection->log, 0,\n                   \"drizzle get peer\");\n\n#if defined(nginx_version) && (nginx_version < 8017)\n    if (data == NULL) {\n        goto failed;\n    }\n\n    dctx = ngx_http_get_module_ctx(dp->request, ngx_http_drizzle_module);\n#endif\n\n    dscf = dp->srv_conf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, dp->request->connection->log, 0,\n                   \"active drizzle connections %ui\", dscf->active_conns);\n\n    dp->failed = 0;\n\n    /* try to get an idle connection from our single-mode\n     * keep-alive pool */\n\n    if (dscf->max_cached && dscf->single) {\n        rc = ngx_http_drizzle_keepalive_get_peer_single(pc, dp, dscf);\n        if (rc != NGX_DECLINED) {\n            return rc;\n        }\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, dp->request->connection->log, 0,\n                   \"drizzle get peer using simple round robin\");\n\n    peers = dscf->peers;\n\n    if (dscf->current > peers->number - 1) {\n        dscf->current = 0;\n    }\n\n    peer = &peers->peer[dscf->current++];\n\n    dp->name.data = peer->name.data;\n    dp->name.len = peer->name.len;\n\n    dp->sockaddr = *peer->sockaddr;\n\n    dp->enable_charset = (peer->set_names_query->len > 0);\n    dp->set_names_query = peer->set_names_query;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"drizzle set connection charset query \\\"%V\\\"\",\n                   dp->set_names_query);\n\n    pc->name = &dp->name;\n    pc->sockaddr = &dp->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->cached = 0;\n\n    if (dscf->max_cached && ! dscf->single) {\n        rc = ngx_http_drizzle_keepalive_get_peer_multi(pc, dp, dscf);\n        if (rc != NGX_DECLINED) {\n            return rc;\n        }\n    }\n\n    if (dscf->overflow == drizzle_keepalive_overflow_reject\n        && dscf->active_conns >= dscf->max_cached)\n    {\n        ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                      \"drizzle: connection pool full, rejecting request \"\n                      \"to upstream \\\"%V\\\"\",\n                      &peer->name);\n\n        /* a bit hack-ish way to return error response (setup part) */\n        pc->connection = ngx_get_connection(0, pc->log);\n\n#if defined(nginx_version) && (nginx_version < 8017)\n        dctx->status = NGX_HTTP_SERVICE_UNAVAILABLE;\n#endif\n\n        return NGX_AGAIN;\n    }\n\n    /* set up the peer's drizzle connection */\n\n    dc = ngx_pcalloc(dscf->pool, sizeof(drizzle_con_st));\n\n    if (dc == NULL) {\n#if defined(nginx_version) && (nginx_version >= 8017)\n        return NGX_ERROR;\n#else\n        goto failed;\n#endif\n    }\n\n    dp->drizzle_con = dc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n        \"drizzle creating connection\");\n\n    (void) drizzle_con_create(&dscf->drizzle, dc);\n\n    /* set protocol for the drizzle connection */\n\n    if (peer->protocol == ngx_http_mysql_protocol) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"drizzle using mysql protocol\");\n\n        drizzle_con_add_options(dc, DRIZZLE_CON_MYSQL);\n\n    }\n\n    /* set dbname for the drizzle connection */\n\n    if (dp->dbname.len) {\n        dbname = dp->dbname;\n\n    } else {\n        dbname = peer->dbname;\n    }\n\n    ngx_memcpy(dc->db, dbname.data, dbname.len);\n    dc->db[dbname.len] = '\\0';\n\n    /* set user for the drizzle connection */\n\n    ngx_memcpy(dc->user, peer->user.data, peer->user.len);\n    dc->user[peer->user.len] = '\\0';\n\n    /* set password for the drizzle connection */\n\n    ngx_memcpy(dc->password, peer->password.data, peer->password.len);\n    dc->password[peer->password.len] = '\\0';\n\n    dd(\"user %s, password %s\", dc->user, dc->password);\n\n    /* TODO add support for uds (unix domain socket) */\n\n    /* set host and port for the drizzle connection */\n\n    drizzle_con_set_tcp(dc, (char *) peer->host, peer->port);\n\n    /* ask drizzle to connect to the remote */\n\n    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"drizzle connecting: host %s, port %d, dbname \\\"%V\\\", \"\n                   \"user \\\"%V\\\", pass \\\"%V\\\", dc pass \\\"%s\\\", \"\n                   \"protocol %d\", peer->host, (int) peer->port, &dbname,\n                   &peer->user, &peer->password, dc->password,\n                   (int) peer->protocol);\n\n    ret = drizzle_con_connect(dc);\n\n    if (ret != DRIZZLE_RETURN_OK && ret != DRIZZLE_RETURN_IO_WAIT) {\n        ngx_log_error(NGX_LOG_EMERG, pc->log, 0,\n                      \"drizzle: failed to connect: %d: %s in upstream \\\"%V\\\"\",\n                      (int) ret,\n                      drizzle_error(&dscf->drizzle),\n                      &peer->name);\n\n        drizzle_con_free(dc);\n        ngx_pfree(dscf->pool, dc);\n\n#if defined(nginx_version) && (nginx_version >= 8017)\n        return NGX_DECLINED;\n#else\n        dctx->status = NGX_HTTP_BAD_GATEWAY;\n        goto failed;\n#endif\n    }\n\n    dscf->active_conns++;\n\n    /* add the file descriptor (fd) into an nginx connection structure */\n\n    fd = drizzle_con_fd(dc);\n\n    if (fd == -1) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                      \"drizzle: failed to get the drizzle connection fd\");\n\n        goto invalid;\n    }\n\n    c = ngx_get_connection(fd, pc->log);\n    if (c == NULL) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                      \"drizzle: failed to get a free nginx connection\");\n\n        goto invalid;\n    }\n\n    c->log = pc->log;\n    c->log_error = pc->log_error;\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = pc->log;\n    wev->log = pc->log;\n\n    pc->connection = c;\n\n    /* register the connection with the drizzle fd into the\n     * nginx event model */\n\n#if 0\n    if (ngx_nonblocking(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n\n        goto invalid;\n    }\n#endif\n\n    if (ret == DRIZZLE_RETURN_IO_WAIT) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"drizzle get peer: still connecting to remote\");\n\n        dp->state = state_db_connect;\n\n        c->log->action = \"connecting to drizzle upstream\";\n\n    } else {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"drizzle get peer: already connected to remote\");\n\n        /* to ensure send_query sets corresponding timers */\n        dp->state = state_db_idle;\n    }\n\n    if (ngx_add_conn) {\n        dd(\"Found ngx_add_conn\");\n\n        if (ngx_add_conn(c) == NGX_ERROR) {\n            goto invalid;\n        }\n\n        if (ret == DRIZZLE_RETURN_IO_WAIT) {\n            dd(\"returned NGX_AGAIN!!!\");\n\n            return NGX_AGAIN;\n        }\n\n        /* ret == DRIZZLE_RETURN_OK */\n\n        wev->ready = 1;\n\n        return NGX_DONE;\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        /* kqueue */\n\n        event = NGX_CLEAR_EVENT;\n\n    } else {\n\n        /* select, poll, /dev/poll */\n\n        event = NGX_LEVEL_EVENT;\n    }\n\n    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                \"drizzle: failed to add connection into nginx event model\");\n\n        goto invalid;\n    }\n\n    if (ret == DRIZZLE_RETURN_IO_WAIT) {\n        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {\n            goto invalid;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, \"drizzle connected\");\n\n    wev->ready = 1;\n\n    /* ret == DRIZZLE_RETURN_OK */\n\n    return NGX_DONE;\n\ninvalid:\n\n    ngx_http_upstream_drizzle_free_connection(pc->log, pc->connection,\n                                              dc, dscf);\n\n#if defined(nginx_version) && (nginx_version >= 8017)\n    return NGX_ERROR;\n#else\n\nfailed:\n\n    /* a bit hack-ish way to return error response (setup part) */\n    pc->connection = ngx_get_connection(0, pc->log);\n\n    return NGX_AGAIN;\n#endif\n}\n\n\nstatic void\nngx_http_upstream_drizzle_free_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state)\n{\n    ngx_http_upstream_drizzle_peer_data_t   *dp = data;\n    ngx_http_upstream_drizzle_srv_conf_t    *dscf;\n\n#if defined(nginx_version) && (nginx_version < 8017)\n    if (data == NULL) {\n        return;\n    }\n#endif\n\n    if (pc && pc->log) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"drizzle free peer\");\n    }\n\n    dscf = dp->srv_conf;\n\n    if (dp->drizzle_con && dp->drizzle_res.con) {\n        dd(\"before drizzle result free\");\n\n        dd(\"%p vs. %p\", dp->drizzle_res.con, dp->drizzle_con);\n\n        drizzle_result_free(&dp->drizzle_res);\n\n        dd(\"after drizzle result free\");\n    }\n\n    if (pc != NULL) {\n        if (dscf->max_cached) {\n            ngx_http_drizzle_keepalive_free_peer(pc, dp, dscf, state);\n        }\n\n        if (pc->connection) {\n            dd(\"actually free the drizzle connection\");\n\n            ngx_http_upstream_drizzle_free_connection(pc->log, pc->connection,\n                                                      dp->drizzle_con, dscf);\n\n            dp->drizzle_con = NULL;\n            pc->connection = NULL;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_drizzle_output_filter(void *data, ngx_chain_t *in)\n{\n    ngx_http_request_t              *r = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n        \"drizzle output filter\");\n\n    /* just to ensure u->reinit_request always gets called for\n     * upstream_next */\n    r->upstream->request_sent = 1;\n\n    (void) ngx_http_drizzle_process_events(r);\n\n    /* discard the ret val from process events because\n     * we can only return NGX_AGAIN here to prevent\n     * ngx_http_upstream_process_header from being called\n     * and avoid u->write_event_handler to be set to\n     * ngx_http_upstream_dummy. */\n\n    return NGX_AGAIN;\n}\n\n\nngx_flag_t\nngx_http_upstream_drizzle_is_my_peer(const ngx_peer_connection_t    *peer)\n{\n    return (peer->get == ngx_http_upstream_drizzle_get_peer);\n}\n\n\nvoid\nngx_http_upstream_drizzle_free_connection(ngx_log_t *log,\n    ngx_connection_t *c, drizzle_con_st *dc,\n    ngx_http_upstream_drizzle_srv_conf_t *dscf)\n{\n    ngx_event_t  *rev, *wev;\n\n    dd(\"drizzle free peer connection\");\n\n    dscf->active_conns--;\n\n    if (dc) {\n        dd(\"before con free\");\n        drizzle_con_free(dc);\n        dd(\"after con free\");\n        ngx_pfree(dscf->pool, dc);\n    }\n\n    if (c) {\n        /* dd(\"c pool: %p\", c->pool); */\n        rev = c->read;\n        wev = c->write;\n\n        if (rev->timer_set) {\n            ngx_del_timer(rev);\n        }\n\n        if (wev->timer_set) {\n            ngx_del_timer(wev);\n        }\n\n        if (ngx_del_conn) {\n            ngx_del_conn(c, NGX_CLOSE_EVENT);\n\n        } else {\n            if (rev->active || rev->disabled) {\n                ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT);\n            }\n\n            if (wev->active || wev->disabled) {\n                ngx_del_event(wev, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);\n            }\n        }\n\n#if defined(nginx_version) && nginx_version >= 1007005\n        if (rev->posted) {\n#else\n        if (rev->prev) {\n#endif\n            ngx_delete_posted_event(rev);\n        }\n\n#if defined(nginx_version) && nginx_version >= 1007005\n        if (wev->posted) {\n#else\n        if (wev->prev) {\n#endif\n            ngx_delete_posted_event(wev);\n        }\n\n        rev->closed = 1;\n        wev->closed = 1;\n\n        ngx_free_connection(c);\n\n        c->fd = (ngx_socket_t) -1;\n    }\n}\n\n\nngx_http_upstream_srv_conf_t *\nngx_http_upstream_drizzle_add(ngx_http_request_t *r, ngx_url_t *url)\n{\n    ngx_http_upstream_main_conf_t  *umcf;\n    ngx_http_upstream_srv_conf_t  **uscfp;\n    ngx_uint_t                      i;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        if (uscfp[i]->host.len != url->host.len\n            || ngx_strncasecmp(uscfp[i]->host.data, url->host.data,\n                               url->host.len) != 0)\n        {\n            dd(\"upstream_add: host not match\");\n            continue;\n        }\n\n        if (uscfp[i]->port != url->port) {\n            dd(\"upstream_add: port not match: %d != %d\",\n               (int) uscfp[i]->port, (int) url->port);\n            continue;\n        }\n\n#if !defined(nginx_version) || nginx_version < 1011006\n        if (uscfp[i]->default_port && url->default_port\n            && uscfp[i]->default_port != url->default_port)\n        {\n            dd(\"upstream_add: default_port not match\");\n            continue;\n        }\n#endif\n\n        return uscfp[i];\n    }\n\n    dd(\"No upstream found: %.*s\", (int) url->host.len, url->host.data);\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_upstream_drizzle_cleanup(void *data)\n{\n    drizzle_st  *drizzle = data;\n\n    drizzle_free(drizzle);\n}\n\n"
  },
  {
    "path": "src/ngx_http_drizzle_upstream.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_UPSTREAM_H\n#define NGX_HTTP_DRIZZLE_UPSTREAM_H\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n#include \"ngx_http_drizzle_module.h\"\n\n\ntypedef enum {\n    drizzle_keepalive_overflow_ignore = 0,\n    drizzle_keepalive_overflow_reject\n\n} ngx_http_drizzle_keepalive_overflow_t;\n\n\ntypedef enum {\n    ngx_http_drizzle_protocol = 0,\n    ngx_http_mysql_protocol\n\n} ngx_http_upstream_drizzle_protocol_t;\n\n\ntypedef struct {\n\n#if defined(nginx_version) && nginx_version >= 8022\n    ngx_addr_t                      *addrs;\n#else\n    ngx_peer_addr_t                 *addrs;\n#endif\n\n    ngx_uint_t                       naddrs;\n    in_port_t                        port;\n    ngx_str_t                        user;\n    ngx_str_t                        password;\n    ngx_str_t                        dbname;\n    ngx_str_t                        set_names_query;\n\n    ngx_http_upstream_drizzle_protocol_t      protocol;\n\n} ngx_http_upstream_drizzle_server_t;\n\n\ntypedef struct {\n    struct sockaddr                *sockaddr;\n    socklen_t                       socklen;\n    ngx_str_t                       name;\n    in_port_t                       port;\n    ngx_str_t                       user;\n    ngx_str_t                       password;\n    ngx_str_t                       dbname;\n    ngx_str_t                      *set_names_query;\n    u_char                         *host;\n\n    ngx_http_upstream_drizzle_protocol_t      protocol;\n\n} ngx_http_upstream_drizzle_peer_t;\n\n\ntypedef struct {\n    unsigned                            single;\n    ngx_uint_t                          number;\n    ngx_str_t                          *name;\n\n    ngx_http_upstream_drizzle_peer_t    peer[1];\n\n} ngx_http_upstream_drizzle_peers_t;\n\n\ntypedef struct {\n    ngx_http_upstream_drizzle_peers_t   *peers;\n\n    /* TODO: we might need \"tried\" from round robin peer data */\n    ngx_uint_t                           current;\n\n    /* of ngx_http_upstream_drizzle_server_t */\n    ngx_array_t                         *servers;\n\n    drizzle_st                           drizzle;\n    ngx_pool_t                          *pool;\n\n    /* keepalive related fields */\n    unsigned                             single;\n    ngx_queue_t                          free;\n    ngx_queue_t                          cache;\n\n    ngx_uint_t                           active_conns;\n\n    ngx_uint_t                           max_cached;\n    ngx_http_drizzle_keepalive_overflow_t    overflow;\n\n} ngx_http_upstream_drizzle_srv_conf_t;\n\n\ntypedef struct {\n    ngx_http_drizzle_loc_conf_t            *loc_conf;\n    ngx_http_upstream_drizzle_srv_conf_t   *srv_conf;\n\n    ngx_http_upstream_t                    *upstream;\n    ngx_http_request_t                     *request;\n\n    ngx_str_t                               dbname;\n    ngx_str_t                               query;\n\n    ngx_http_drizzle_state_t                state;\n\n    drizzle_con_st                         *drizzle_con;\n    drizzle_result_st                       drizzle_res;\n    drizzle_column_st                       drizzle_col;\n    uint64_t                                drizzle_row;\n\n    ngx_str_t                               name;\n\n    ngx_chain_t                           **last_out;\n\n    ngx_buf_t                              *out_buf;\n    ngx_buf_t                               cached;\n    ngx_buf_t                               postponed;\n    size_t                                  avail_out;\n    ngx_str_t                               charset;\n    ngx_str_t                              *set_names_query;\n\n    struct sockaddr                         sockaddr;\n\n    /* how many times this connection has been successfully used */\n    ngx_uint_t                              used;\n\n    unsigned                                failed:1;\n    unsigned                                seen_stream_end:1;\n    unsigned                                has_set_names:1;\n    unsigned                                enable_charset:1;\n} ngx_http_upstream_drizzle_peer_data_t;\n\n\nchar *ngx_http_upstream_drizzle_server(ngx_conf_t *cf, ngx_command_t *cmd,\n        void *conf);\nvoid *ngx_http_upstream_drizzle_create_srv_conf(ngx_conf_t *cf);\nngx_flag_t ngx_http_upstream_drizzle_is_my_peer(\n        const ngx_peer_connection_t *peer);\nvoid ngx_http_upstream_drizzle_free_connection(ngx_log_t *log,\n        ngx_connection_t *c, drizzle_con_st *dc,\n        ngx_http_upstream_drizzle_srv_conf_t *dscf);\nngx_http_upstream_srv_conf_t *ngx_http_upstream_drizzle_add(\n        ngx_http_request_t *r, ngx_url_t *url);\n\n\n#endif /* NGX_HTTP_DRIZZLE_UPSTREAM_H */\n"
  },
  {
    "path": "src/ngx_http_drizzle_util.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef DDEBUG\n#define DDEBUG 0\n#endif\n#include \"ddebug.h\"\n\n\n#include \"ngx_http_drizzle_module.h\"\n#include \"ngx_http_drizzle_util.h\"\n#include \"ngx_http_drizzle_handler.h\"\n#include \"ngx_http_drizzle_processor.h\"\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_upstream_dbd_reinit(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_dbd_handler(ngx_event_t *ev);\nstatic void ngx_http_upstream_dbd_connect(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_dbd_cleanup(void *data);\nstatic void ngx_http_upstream_dbd_wr_check_broken_connection(\n    ngx_http_request_t *r);\nstatic void ngx_http_upstream_dbd_rd_check_broken_connection(\n    ngx_http_request_t *r);\nstatic void ngx_http_upstream_dbd_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev);\n\n\nvoid *\nngx_http_drizzle_get_peer_data(ngx_http_request_t *r)\n{\n    ngx_http_drizzle_ctx_t  *dctx;\n\n    /* Prefer the ctx-saved pointer because r->upstream->peer.data may have\n     * been wrapped by the standard upstream keepalive (implicit since\n     * nginx 1.29.7) or by another balancer module. Fall back to peer.data\n     * for the rare case where the ctx is missing. */\n    dctx = ngx_http_get_module_ctx(r, ngx_http_drizzle_module);\n    if (dctx != NULL && dctx->peer_data != NULL) {\n        return dctx->peer_data;\n    }\n\n    return r->upstream ? r->upstream->peer.data : NULL;\n}\n\n\nngx_int_t\nngx_http_drizzle_set_header(ngx_http_request_t *r, ngx_str_t *key,\n    ngx_str_t *value)\n{\n    ngx_uint_t                   i;\n    ngx_table_elt_t             *h;\n    ngx_list_part_t             *part;\n\n    dd(\"entered set_header\");\n\n    part = &r->headers_out.headers.part;\n    h = part->elts;\n\n    for (i = 0; /* void */; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        if (h[i].key.len == key->len\n            && ngx_strncasecmp(h[i].key.data, key->data, h[i].key.len) == 0)\n        {\n            if (value->len == 0) {\n                h[i].hash = 0;\n            }\n\n            h[i].value = *value;\n\n            return NGX_OK;\n        }\n    }\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\n    h = ngx_list_push(&r->headers_out.headers);\n\n    if (h == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    h->hash = 1;\n    h->key = *key;\n    h->value = *value;\n\n    return NGX_OK;\n}\n\n\n/* the following functions are copied directly from\n   ngx_http_upstream.c in nginx 0.8.30, just because\n   they're static. sigh. */\n\nvoid\nngx_http_upstream_drizzle_finalize_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc)\n{\n#if !defined(nginx_version) || nginx_version < 1009001\n    ngx_time_t  *tp;\n#endif\n\n    dd(\"enter\");\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http upstream request: %i\", rc);\n\n    if (u->cleanup) {\n        *u->cleanup = NULL;\n    }\n\n    if (u->resolved && u->resolved->ctx) {\n        ngx_resolve_name_done(u->resolved->ctx);\n        u->resolved->ctx = NULL;\n    }\n\n#if defined(nginx_version) && nginx_version >= 1009001\n\n    if (u->state && u->state->response_time) {\n        u->state->response_time = ngx_current_msec - u->state->response_time;\n\n#else\n\n    if (u->state && u->state->response_sec) {\n        tp = ngx_timeofday();\n        u->state->response_sec = tp->sec - u->state->response_sec;\n        u->state->response_msec = tp->msec - u->state->response_msec;\n\n#endif\n\n        if (u->pipe) {\n            u->state->response_length = u->pipe->read_length;\n        }\n    }\n\n    if (u->finalize_request) {\n        u->finalize_request(r, rc);\n    }\n\n    if (u->peer.free) {\n        dd(\"before free peer\");\n        u->peer.free(&u->peer, u->peer.data, 0);\n        dd(\"after free peer\");\n    }\n\n    dd(\"about to free peer 2, c: %p, r pool: %p\", u->peer.connection, r->pool);\n\n    if (u->peer.connection) {\n\n#if 0 /* libdrizzle doesn't support SSL, was: (NGX_HTTP_SSL) */\n\n        /* TODO: do not shutdown persistent connection */\n\n        if (u->peer.connection->ssl) {\n\n            /*\n             * We send the \"close notify\" shutdown alert to the upstream only\n             * and do not wait its \"close notify\" shutdown alert.\n             * It is acceptable according to the TLS standard.\n             */\n\n            u->peer.connection->ssl->no_wait_shutdown = 1;\n\n            (void) ngx_ssl_shutdown(u->peer.connection);\n        }\n#endif\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"drizzle close http upstream connection: %d\",\n                       u->peer.connection->fd);\n\n        dd(\"r pool: %p, c pool: %p\", r->pool, u->peer.connection->pool);\n\n        ngx_close_connection(u->peer.connection);\n    }\n\n    u->peer.connection = NULL;\n\n    if (u->pipe && u->pipe->temp_file) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http upstream temp fd: %d\",\n                       u->pipe->temp_file->file.fd);\n    }\n\n    if (u->header_sent\n        && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))\n    {\n        rc = 0;\n    }\n\n    if (rc == NGX_DECLINED) {\n        return;\n    }\n\n    r->connection->log->action = \"sending to client\";\n\n    if (rc == 0) {\n        rc = ngx_http_send_special(r, NGX_HTTP_LAST);\n    }\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nvoid\nngx_http_upstream_drizzle_next(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t ft_type)\n{\n    ngx_uint_t  status, state;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http next upstream, %xi\", ft_type);\n\n#if 0\n    ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);\n#endif\n\n    if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) {\n        state = NGX_PEER_NEXT;\n    } else {\n        state = NGX_PEER_FAILED;\n    }\n\n    if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {\n        u->peer.free(&u->peer, u->peer.data, state);\n    }\n\n    if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,\n                      \"upstream timed out\");\n    }\n\n    if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {\n        status = 0;\n\n    } else {\n        switch (ft_type) {\n\n        case NGX_HTTP_UPSTREAM_FT_TIMEOUT:\n            status = NGX_HTTP_GATEWAY_TIME_OUT;\n            break;\n\n        case NGX_HTTP_UPSTREAM_FT_HTTP_500:\n            status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            break;\n\n        case NGX_HTTP_UPSTREAM_FT_HTTP_404:\n            status = NGX_HTTP_NOT_FOUND;\n            break;\n\n        /*\n         * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING\n         * never reach here\n         */\n\n        default:\n            status = NGX_HTTP_BAD_GATEWAY;\n        }\n    }\n\n    if (r->connection->error) {\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    if (status) {\n        u->state->status = status;\n\n        if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) {\n            ngx_http_upstream_drizzle_finalize_request(r, u, status);\n            return;\n        }\n    }\n\n    if (u->peer.connection) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"close http upstream connection: %d\",\n                       u->peer.connection->fd);\n#if 0 /* libdrizzle doesn't support SSL, was: (NGX_HTTP_SSL) */\n\n        if (u->peer.connection->ssl) {\n            u->peer.connection->ssl->no_wait_shutdown = 1;\n            u->peer.connection->ssl->no_send_shutdown = 1;\n\n            (void) ngx_ssl_shutdown(u->peer.connection);\n        }\n#endif\n\n        dd(\"r pool: %p, c pool: %p\", r->pool, u->peer.connection->pool);\n\n        ngx_close_connection(u->peer.connection);\n    }\n\n#if 0\n    if (u->conf->busy_lock && !u->busy_locked) {\n        ngx_http_upstream_busy_lock(p);\n        return;\n    }\n#endif\n\n    /* TODO: ngx_http_upstream_connect(r, u); */\n    if (status == 0) {\n        status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_upstream_drizzle_finalize_request(r, u, status);\n}\n\n\nngx_int_t\nngx_http_upstream_drizzle_test_connect(ngx_connection_t *c)\n{\n    int        err;\n    socklen_t  len;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        if (c->write->pending_eof) {\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, c->write->kq_errno,\n                                        \"kevent() reported that connect() \"\n                                        \"failed\");\n            return NGX_ERROR;\n        }\n\n    } else\n#endif\n    {\n        err = 0;\n        len = sizeof(int);\n\n        /*\n         * BSDs and Linux return 0 and set a pending error in err\n         * Solaris returns -1 and sets errno\n         */\n\n        if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)\n            == -1)\n        {\n            err = ngx_errno;\n        }\n\n        if (err) {\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err, \"connect() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_upstream_dbd_init(ngx_http_request_t *r)\n{\n    ngx_connection_t     *c;\n\n    c = r->connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http init upstream, client timer: %d\", c->read->timer_set);\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        if (!c->write->active) {\n            if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)\n                == NGX_ERROR)\n            {\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n    }\n\n    ngx_http_upstream_dbd_init_request(r);\n}\n\n\nvoid\nngx_http_upstream_dbd_init_request(ngx_http_request_t *r)\n{\n    ngx_str_t                      *host;\n    ngx_uint_t                      i;\n    ngx_resolver_ctx_t             *ctx, temp;\n    ngx_http_cleanup_t             *cln;\n    ngx_http_upstream_t            *u;\n    ngx_http_core_loc_conf_t       *clcf;\n    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n#if defined(nginx_version) && nginx_version >= 8011\n    if (r->aio) {\n        return;\n    }\n#endif\n\n    u = r->upstream;\n\n#if 0 && (NGX_HTTP_CACHE)\n\n    if (u->conf->cache) {\n        ngx_int_t  rc;\n\n        rc = ngx_http_upstream_cache(r, u);\n\n        if (rc == NGX_BUSY) {\n            r->write_event_handler = ngx_http_upstream_init_request;\n            return;\n        }\n\n        r->write_event_handler = ngx_http_request_empty_handler;\n\n        if (rc == NGX_DONE) {\n            return;\n        }\n\n        if (rc != NGX_DECLINED) {\n            ngx_http_finalize_request(r, rc);\n            return;\n        }\n    }\n\n#endif\n\n    u->store = (u->conf->store || u->conf->store_lengths);\n\n    if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {\n        r->read_event_handler =\n                             ngx_http_upstream_dbd_rd_check_broken_connection;\n        r->write_event_handler =\n                             ngx_http_upstream_dbd_wr_check_broken_connection;\n    }\n\n    if (r->request_body) {\n        u->request_bufs = r->request_body->bufs;\n    }\n\n    if (u->create_request(r) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n#if 0\n#if defined(nginx_version) && nginx_version >= 8022\n    u->peer.local = u->conf->local;\n#endif\n#endif\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if defined(nginx_version) && nginx_version >= 8011\n    u->output.alignment = clcf->directio_alignment;\n#endif\n\n    u->output.pool = r->pool;\n    u->output.bufs.num = 1;\n    u->output.bufs.size = clcf->client_body_buffer_size;\n    u->output.output_filter = ngx_chain_writer;\n    u->output.filter_ctx = &u->writer;\n\n    u->writer.pool = r->pool;\n\n    if (r->upstream_states == NULL) {\n\n        r->upstream_states = ngx_array_create(r->pool, 1,\n                                           sizeof(ngx_http_upstream_state_t));\n        if (r->upstream_states == NULL) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n    } else {\n\n        u->state = ngx_array_push(r->upstream_states);\n        if (u->state == NULL) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));\n    }\n\n    cln = ngx_http_cleanup_add(r, 0);\n    if (cln == NULL) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    cln->handler = ngx_http_upstream_dbd_cleanup;\n    cln->data = r;\n    u->cleanup = &cln->handler;\n\n    if (u->resolved == NULL) {\n\n        uscf = u->conf->upstream;\n\n    } else {\n\n        if (u->resolved->sockaddr) {\n\n            if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)\n                != NGX_OK)\n            {\n                ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            ngx_http_upstream_dbd_connect(r, u);\n\n            return;\n        }\n\n        host = &u->resolved->host;\n\n        umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n        uscfp = umcf->upstreams.elts;\n\n        for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n            uscf = uscfp[i];\n\n            if (uscf->host.len == host->len\n                && ((uscf->port == 0 && u->resolved->no_port)\n                    || uscf->port == u->resolved->port)\n                && ngx_memcmp(uscf->host.data, host->data, host->len) == 0)\n            {\n                goto found;\n            }\n        }\n\n        temp.name = *host;\n\n        ctx = ngx_resolve_start(clcf->resolver, &temp);\n        if (ctx == NULL) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ctx == NGX_NO_RESOLVER) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"no resolver defined to resolve %V\", host);\n\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                                       NGX_HTTP_BAD_GATEWAY);\n\n            return;\n        }\n\n#if 0\n        ctx->name = *host;\n        ctx->type = NGX_RESOLVE_A;\n        ctx->handler = ngx_http_upstream_resolve_handler;\n        ctx->data = r;\n        ctx->timeout = clcf->resolver_timeout;\n\n        u->resolved->ctx = ctx;\n\n        if (ngx_resolve_name(ctx) != NGX_OK) {\n            u->resolved->ctx = NULL;\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n#endif\n        return;\n    }\n\nfound:\n\n    if (uscf->peer.init(r, uscf) != NGX_OK) {\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_http_upstream_dbd_connect(r, u);\n}\n\n\nstatic void\nngx_http_upstream_dbd_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_int_t          rc;\n#if !defined(nginx_version) || nginx_version < 1009001\n    ngx_time_t        *tp;\n#endif\n    ngx_connection_t  *c;\n\n    r->connection->log->action = \"connecting to upstream\";\n\n#if defined(nginx_version) && nginx_version >= 1009001\n\n    if (u->state && u->state->response_time) {\n        u->state->response_time = ngx_current_msec - u->state->response_time;\n    }\n\n#else\n\n    if (u->state && u->state->response_sec) {\n        tp = ngx_timeofday();\n\n        u->state->response_sec = tp->sec - u->state->response_sec;\n        u->state->response_msec = tp->msec - u->state->response_msec;\n    }\n\n#endif\n\n    u->state = ngx_array_push(r->upstream_states);\n    if (u->state == NULL) {\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));\n\n#if defined(nginx_version) && nginx_version >= 1009001\n\n    u->state->response_time = ngx_current_msec;\n    u->state->connect_time = (ngx_msec_t) -1;\n    u->state->header_time = (ngx_msec_t) -1;\n\n#else\n\n    tp = ngx_timeofday();\n    u->state->response_sec = tp->sec;\n    u->state->response_msec = tp->msec;\n\n#endif\n\n    rc = ngx_event_connect_peer(&u->peer);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream connect: %i\", rc);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->state->peer = u->peer.name;\n\n    if (rc == NGX_BUSY) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"no live upstreams\");\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);\n        return;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_http_upstream_drizzle_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    /* rc == NGX_OK || rc == NGX_AGAIN */\n\n    c = u->peer.connection;\n\n    c->data = r;\n\n    c->write->handler = ngx_http_upstream_dbd_handler;\n    c->read->handler = ngx_http_upstream_dbd_handler;\n\n    u->write_event_handler = ngx_http_drizzle_wev_handler;\n    u->read_event_handler = ngx_http_drizzle_rev_handler;\n\n    c->sendfile &= r->connection->sendfile;\n    u->output.sendfile = c->sendfile;\n\n    c->pool = r->pool;\n    c->log = r->connection->log;\n    c->read->log = c->log;\n    c->write->log = c->log;\n\n    /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */\n\n    u->writer.out = NULL;\n    u->writer.last = &u->writer.out;\n    u->writer.connection = c;\n    u->writer.limit = 0;\n\n    if (u->request_sent) {\n        if (ngx_http_upstream_dbd_reinit(r, u) != NGX_OK) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (r->request_body\n        && r->request_body->buf\n        && r->request_body->temp_file\n        && r == r->main)\n    {\n        /*\n         * the r->request_body->buf can be reused for one request only,\n         * the subrequests should allocate their own temporay bufs\n         */\n\n        u->output.free = ngx_alloc_chain_link(r->pool);\n        if (u->output.free == NULL) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->output.free->buf = r->request_body->buf;\n        u->output.free->next = NULL;\n        u->output.allocated = 1;\n\n        r->request_body->buf->pos = r->request_body->buf->start;\n        r->request_body->buf->last = r->request_body->buf->start;\n        r->request_body->buf->tag = u->output.tag;\n    }\n\n    u->request_sent = 0;\n\n    if (rc == NGX_AGAIN) {\n        ngx_add_timer(c->write, u->conf->connect_timeout);\n        return;\n    }\n\n#if 0 /* libdrizzle doesn't support SSL, was: (NGX_HTTP_SSL) */\n\n    if (u->ssl && c->ssl == NULL) {\n        ngx_http_upstream_ssl_init_connection(r, u, c);\n        return;\n    }\n\n#endif\n\n#if 0\n    ngx_http_upstream_send_request(r, u);\n#endif\n\n    dd(\"connection error: %d\", c->error);\n\n    ngx_http_drizzle_set_libdrizzle_ready(r);\n\n    (void) ngx_http_drizzle_process_events(r);\n}\n\n\nstatic void\nngx_http_upstream_dbd_rd_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_upstream_dbd_check_broken_connection(r, r->connection->read);\n}\n\n\nstatic void\nngx_http_upstream_dbd_wr_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_upstream_dbd_check_broken_connection(r, r->connection->write);\n}\n\n\nstatic void\nngx_http_upstream_dbd_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev)\n{\n    int                  n;\n    char                 buf[1];\n    ngx_err_t            err;\n    ngx_int_t            event;\n    ngx_connection_t     *c;\n    ngx_http_upstream_t  *u;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"http upstream check client, write event:%d, \\\"%V\\\"\",\n                   ev->write, &r->uri);\n\n    c = r->connection;\n    u = r->upstream;\n\n    if (c->error) {\n        if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n\n            event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n\n            if (ngx_del_event(ev, event, 0) != NGX_OK) {\n                ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n\n        if (!u->cacheable) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                              NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n\n        if (!ev->pending_eof) {\n            return;\n        }\n\n        ev->eof = 1;\n        c->error = 1;\n\n        if (ev->kq_errno) {\n            ev->error = 1;\n        }\n\n        if (!u->cacheable && u->peer.connection) {\n            ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                          \"kevent() reported that client closed prematurely \"\n                          \"connection, so upstream connection is closed too\");\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n            return;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                      \"kevent() reported that client closed \"\n                      \"prematurely connection\");\n\n        if (u->peer.connection == NULL) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\n    }\n\n#endif\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    err = ngx_socket_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,\n                   \"http upstream recv(): %d\", n);\n\n    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {\n        return;\n    }\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n\n        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n\n        if (ngx_del_event(ev, event, 0) != NGX_OK) {\n            ngx_http_upstream_drizzle_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (n > 0) {\n        return;\n    }\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            return;\n        }\n\n        ev->error = 1;\n\n    } else { /* n == 0 */\n        err = 0;\n    }\n\n    ev->eof = 1;\n    c->error = 1;\n\n    if (!u->cacheable && u->peer.connection) {\n        ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                      \"client closed prematurely connection, \"\n                      \"so upstream connection is closed too\");\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                  \"client closed prematurely connection\");\n\n    if (u->peer.connection == NULL) {\n        ngx_http_upstream_drizzle_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n    }\n}\n\n\nstatic void\nngx_http_upstream_dbd_cleanup(void *data)\n{\n    ngx_http_request_t *r = data;\n\n    ngx_http_upstream_t  *u;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"cleanup http upstream request: \\\"%V\\\"\", &r->uri);\n\n    u = r->upstream;\n\n    if (u->resolved && u->resolved->ctx) {\n        ngx_resolve_name_done(u->resolved->ctx);\n        u->resolved->ctx = NULL;\n    }\n\n    ngx_http_upstream_drizzle_finalize_request(r, u, NGX_DONE);\n}\n\n\nstatic void\nngx_http_upstream_dbd_handler(ngx_event_t *ev)\n{\n    ngx_connection_t     *c;\n    ngx_http_request_t   *r;\n    ngx_http_log_ctx_t   *ctx;\n    ngx_http_upstream_t  *u;\n\n    c = ev->data;\n    r = c->data;\n\n    u = r->upstream;\n    c = r->connection;\n\n    ctx = c->log->data;\n    ctx->current_request = r;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream request: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    dd(\"upstream ev write:%d, ready:%d\", (int) ev->write, (int) ev->ready);\n\n    if (ev->write) {\n        u->write_event_handler(r, u);\n\n    } else {\n        u->read_event_handler(r, u);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_dbd_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_chain_t  *cl;\n\n    if (u->reinit_request(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));\n\n    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    /* reinit the request chain */\n\n    for (cl = u->request_bufs; cl; cl = cl->next) {\n        cl->buf->pos = cl->buf->start;\n        cl->buf->file_pos = 0;\n    }\n\n    /* reinit the subrequest's ngx_output_chain() context */\n\n    if (r->request_body && r->request_body->temp_file\n        && r != r->main && u->output.buf)\n    {\n        u->output.free = ngx_alloc_chain_link(r->pool);\n        if (u->output.free == NULL) {\n            return NGX_ERROR;\n        }\n\n        u->output.free->buf = u->output.buf;\n        u->output.free->next = NULL;\n\n        u->output.buf->pos = u->output.buf->start;\n        u->output.buf->last = u->output.buf->start;\n    }\n\n    u->output.buf = NULL;\n    u->output.in = NULL;\n    u->output.busy = NULL;\n\n    /* reinit u->buffer */\n\n    u->buffer.pos = u->buffer.start;\n\n#if 0 && (NGX_HTTP_CACHE)\n\n    if (r->cache) {\n        u->buffer.pos += r->cache->header_start;\n    }\n\n#endif\n\n    u->buffer.last = u->buffer.pos;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_drizzle_set_thread_id_variable(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_http_drizzle_loc_conf_t    *dlcf;\n    size_t                          size;\n    ngx_http_variable_value_t      *vv;\n    uint32_t                        tid;\n    drizzle_con_st                 *dc;\n\n    ngx_http_upstream_drizzle_peer_data_t       *dp;\n\n    dp = ngx_http_drizzle_get_peer_data(r);\n    if (dp == NULL) {\n        return NGX_ERROR;\n    }\n\n    dc = dp->drizzle_con;\n    if (dc == NULL) {\n        return NGX_ERROR;\n    }\n\n    tid = drizzle_con_thread_id(dc);\n\n    dd(\"tid = %d\", (int) tid);\n\n    if (tid == 0) {\n        /* invalid thread id */\n        return NGX_OK;\n    }\n\n    size = ngx_http_drizzle_get_num_size(tid);\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_drizzle_module);\n\n    vv = ngx_http_get_indexed_variable(r, dlcf->tid_var_index);\n\n    if (vv == NULL) {\n        return NGX_ERROR;\n    }\n\n    vv->not_found = 0;\n    vv->valid = 1;\n    vv->no_cacheable = 0;\n\n    vv->data = ngx_palloc(r->pool, size);\n    if (vv->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    vv->len = size;\n\n    ngx_sprintf(vv->data, \"%uD\", tid);\n\n    dd(\"$drizzle_thread_id set\");\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_drizzle_get_num_size(uint64_t i)\n{\n    size_t          n = 0;\n\n    do {\n        i = i / 10;\n        n++;\n    } while (i > 0);\n\n    return n;\n}\n\n\nngx_uint_t\nngx_http_drizzle_queue_size(ngx_queue_t *queue)\n{\n    ngx_queue_t     *q;\n    ngx_uint_t       n = 0;\n\n    for (q = ngx_queue_head(queue);\n         q != ngx_queue_sentinel(queue);\n         q = ngx_queue_next(q))\n    {\n        n++;\n    }\n\n    return n;\n}\n"
  },
  {
    "path": "src/ngx_http_drizzle_util.h",
    "content": "#ifndef NGX_HTTP_DRIZZLE_UTIL_H\n#define NGX_HTTP_DRIZZLE_UTIL_H\n\n#include \"ddebug.h\"\n\n#include <nginx.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#define ngx_http_drizzle_strcmp_const(a, b)                                  \\\n        ngx_strncmp(a, b, sizeof(b) - 1)\n\n#ifndef ngx_copy_const_str\n#define ngx_copy_const_str(p, s)  ngx_copy(p, s, sizeof(s) - 1)\n#endif\n\n\n/* Returns the drizzle peer-data pointer (ngx_http_upstream_drizzle_peer_data_t *).\n * Returns void * to avoid pulling ngx_http_drizzle_upstream.h here. */\nvoid *ngx_http_drizzle_get_peer_data(ngx_http_request_t *r);\nvoid ngx_http_upstream_dbd_init(ngx_http_request_t *r);\nvoid ngx_http_upstream_dbd_init_request(ngx_http_request_t *r);\nngx_int_t ngx_http_drizzle_set_header(ngx_http_request_t *r, ngx_str_t *key,\n        ngx_str_t *value);\nvoid ngx_http_upstream_drizzle_finalize_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc);\nvoid ngx_http_upstream_drizzle_next(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc);\nngx_int_t ngx_http_upstream_drizzle_test_connect(ngx_connection_t *c);\nngx_int_t ngx_http_drizzle_set_thread_id_variable(ngx_http_request_t *r,\n        ngx_http_upstream_t *u);\nngx_uint_t ngx_http_drizzle_queue_size(ngx_queue_t *queue);\nsize_t ngx_http_drizzle_get_num_size(uint64_t i);\n\n\n#define ngx_http_drizzle_nelems(x) (sizeof(x) / sizeof(x[0]))\n\n\n#endif /* NGX_HTTP_DRIZZLE_UTIL_H */\n\n"
  },
  {
    "path": "src/resty_dbd_stream.h",
    "content": "#ifndef RESTY_DBD_STREAME_H\n#define RESTY_DBD_STREAME_H\n\n#define resty_dbd_stream_version 3\n#define resty_dbd_stream_version_string \\\n    \"0.0.3\"\n\n#define rds_content_type \\\n    \"application/x-resty-dbd-stream\"\n\n#define rds_content_type_len \\\n    (sizeof(rds_content_type) - 1)\n\n\ntypedef enum {\n    rds_rough_col_type_int = 0 << 14,\n    rds_rough_col_type_float = 1 << 14,\n    rds_rough_col_type_str = 2 << 14,\n    rds_rough_col_type_bool = 3 << 14\n\n} rds_rough_col_type_t;\n\n\n/* The following types (or spellings thereof) are specified\n * by SQL:\n * bigint, bit, bit varying, boolean, char, character varying,\n * character, varchar, date, double precision, integer,\n * interval, numeric, decimal, real, smallint,\n * time (with or without time zone),\n * timestamp (with or without time zone), xml */\n\ntypedef enum {\n    rds_col_type_unknown = 0 | rds_rough_col_type_str,\n    rds_col_type_bigint = 1 | rds_rough_col_type_int,\n    rds_col_type_bit = 2 | rds_rough_col_type_str,\n    rds_col_type_bit_varying = 3 | rds_rough_col_type_str,\n\n    rds_col_type_bool = 4 | rds_rough_col_type_bool,\n    rds_col_type_char = 5 | rds_rough_col_type_str,\n    rds_col_type_varchar = 6 | rds_rough_col_type_str,\n    rds_col_type_date = 7 | rds_rough_col_type_str,\n    rds_col_type_double = 8 | rds_rough_col_type_float,\n    rds_col_type_integer = 9 | rds_rough_col_type_int,\n    rds_col_type_interval = 10 | rds_rough_col_type_float,\n    rds_col_type_decimal = 11 | rds_rough_col_type_float,\n    rds_col_type_real = 12 | rds_rough_col_type_float,\n    rds_col_type_smallint = 13 | rds_rough_col_type_int,\n    rds_col_type_time_with_time_zone = 14 | rds_rough_col_type_str,\n    rds_col_type_time = 15 | rds_rough_col_type_str,\n    rds_col_type_timestamp_with_time_zone = 16 | rds_rough_col_type_str,\n    rds_col_type_timestamp = 17 | rds_rough_col_type_str,\n    rds_col_type_xml = 18 | rds_rough_col_type_str,\n\n    /* our additions */\n    rds_col_type_blob = 19 | rds_rough_col_type_str\n\n} rds_col_type_t;\n\n#endif /* RESTY_DBD_STREAME_H */\n\n"
  },
  {
    "path": "t/000_init.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(1);\n\nplan tests => repeat_each() * blocks();\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream database {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n_EOC_\n\nworker_connections(128);\nno_shuffle();\n#log_level 'warn';\nno_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: cats - drop table\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"DROP TABLE IF EXISTS cats\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 2: cats - create table\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"CREATE TABLE cats (id integer, name text)\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 3: cats - insert value\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"INSERT INTO cats (id) VALUES (2)\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 4: cats - insert value\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"INSERT INTO cats (id, name) VALUES (3, 'bob')\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n"
  },
  {
    "path": "t/bugs.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() + 1);\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream foo {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: two locations\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update table_that_doesnt_exist set name='bob'\";\n    }\n    location /mysql2 {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update table_that_doesnt_exist set name='bob'\";\n    }\n\n--- request\nGET /mysql\n--- error_code: 410\n--- response_body_like: 410 Gone\n--- timeout: 5\n\n"
  },
  {
    "path": "t/charset/000_init.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(1);\n\nplan tests => repeat_each() * blocks();\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream database {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n    }\n_EOC_\n\nworker_connections(128);\nno_shuffle();\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: cats - drop table\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"DROP TABLE IF EXISTS cats\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 2: cats - create table\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"CREATE TABLE cats (id integer, name text)\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 3: cats - insert value\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"INSERT INTO cats (id) VALUES (2)\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n\n\n\n=== TEST 4: cats - insert value\n--- http_config eval: $::http_config\n--- config\n    location = /init {\n        drizzle_pass   database;\n        drizzle_query  \"INSERT INTO cats (id, name) VALUES (3, 'bob')\";\n    }\n--- request\nGET /init\n--- error_code: 200\n--- timeout: 10\n"
  },
  {
    "path": "t/charset/errors.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() + 2);\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream foo {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: bad query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update table_that_doesnt_exist set name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 410\n--- response_body_like: 410 Gone\n--- timeout: 5\n\n\n\n=== TEST 2: wrong credentials\nlittle-endian systems only\n\n--- timeout: 5\n--- http_config\n    upstream foo {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT dbname=test\n             password=wrong_pass user=monty protocol=mysql\n             charset=utf8;\n        drizzle_keepalive mode=single max=2 overflow=reject;\n    }\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 502\n\n\n\n=== TEST 3: no database\nlittle-endian systems only\n\n--- http_config\n    upstream foo {\n        drizzle_server 127.0.0.1:1 dbname=test\n             password=some_pass user=monty protocol=mysql\n             charset=utf8;\n        drizzle_keepalive mode=single max=2 overflow=reject;\n    }\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 502\n--- timeout: 1\n\n\n\n=== TEST 4: multiple queries\nlittle-endian systems only\n\n--- timeout: 5\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"select * from cats; select * from cats\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 5: missing query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 6: empty query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        set $query \"\";\n        drizzle_query $query;\n    }\n--- request\nGET /mysql\n--- error_code: 500\n--- timeout: 5\n\n\n\n=== TEST 7: empty pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend \"\";\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 8: empty pass\nlittle-endian systems only\n\n--- http_config\n    upstream foo {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=blah-blah;\n    }\n\n--- config\n    location /mysql {\n        drizzle_pass foo;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n--- response_body_like: 500 Internal Server Error\n\n"
  },
  {
    "path": "t/charset/keepalive.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 2 * blocks() + 2 * repeat_each() * 6;\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n        drizzle_keepalive max=10 overflow=ignore mode=single;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: sanity\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 2: keep-alive\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 3: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 4: select empty result\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_query \"select * from cats where name='tom'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 5: update & no module header\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 6: variables in drizzle_pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $foo backend;\n        drizzle_pass $foo;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 7: sanity (using little bufs, size 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 1;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 8: sanity (using little bufs, size 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 2;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 9: sanity (using little bufs, size 3)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 3;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 10: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n"
  },
  {
    "path": "t/charset/methods.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() * 3 - 2 * 2);\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream database {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: default query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{07}\\x{00}\\x{00}\\x{00}\".  # field len\n\"default\".       # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 2: method-specific query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select 'GET' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 3: method-specific complex query (check 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 4: method-specific complex query (check 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nLOCK /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{04}\\x{00}\\x{00}\\x{00}\".  # field len\n\"LOCK\".          # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 5: method-specific complex query (using not allowed method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nHEAD /mysql\n--- error_code: 405\n--- timeout: 10\n\n\n\n=== TEST 6: method-specific query and default query (using defined method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 7: method-specific query and default query (using other method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nPOST /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{07}\\x{00}\\x{00}\\x{00}\".  # field len\n\"default\".       # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 8: inheritance\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    drizzle_query      \"select 'default' as echo\";\n    drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n\n    location /mysql {\n        drizzle_pass       database;\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 9: inheritance (mixed, not inherited)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    drizzle_query      \"select 'default' as echo\";\n\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nHEAD /mysql\n--- error_code: 405\n--- timeout: 10\n\n\n\n=== TEST 10: default query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select '$request_method' as echo\";\n    }\n--- request\nPATCH /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{05}\\x{00}\\x{00}\\x{00}\".  # field len\n\"PATCH\".         # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n--- skip_nginx: 3: < 0.8.41\n"
  },
  {
    "path": "t/charset/sanity.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * 2 * blocks() + 2 * repeat_each() * 6;\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n        #drizzle_keepalive max=10 overflow=ignore mode=single;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: sanity\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 2: keep-alive\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 3: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 4: select empty result\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_query \"select * from cats where name='tom'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 5: update & no module header\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 6: variables in drizzle_pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $foo backend;\n        drizzle_pass $foo;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 7: sanity (using little bufs, size 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 1;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 8: sanity (using little bufs, size 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 2;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 9: sanity (using little bufs, size 3)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 3;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 10: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n"
  },
  {
    "path": "t/charset/timeout.t",
    "content": "# vi:ft=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\n#repeat_each(2);\nrepeat_each(1);\n\nplan tests => repeat_each() * blocks() * 2;\n\nour $http_config = <<'_EOC_';\n    upstream foo {\n        drizzle_server www.taobao.com:1234;\n    }\n_EOC_\n\nworker_connections(128);\n#log_level('error');\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n\nno_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: loc_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n        drizzle_connect_timeout 10ms;\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 2: http_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    drizzle_connect_timeout 3;\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n        drizzle_connect_timeout 10ms;\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 3: serv_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    drizzle_connect_timeout 10ms;\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 4: serv_config connect timeout\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test\n                       charset=utf8;\n    }\n\n--- config\n    #drizzle_connect_timeout 1;\n    drizzle_send_query_timeout 10ms;\n    #drizzle_recv_cols_timeout 10ms;\n    #drizzle_recv_rows_timeout 10ms;\n\n    location /upstream {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query 'select sql_no_cache * from cats as a, cats as b, cats as c, cats as d, cats as e, cats as f, cats as g, cats as h, cats as i, cats as j order by a.id, b.id, c.id, d.id';\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 1\n--- SKIP\n\n"
  },
  {
    "path": "t/errors.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (blocks() + 1);\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream foo {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: bad query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update table_that_doesnt_exist set name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 410\n--- response_body_like: 410 Gone\n--- timeout: 5\n\n\n\n=== TEST 2: wrong credentials\nlittle-endian systems only\n\n--- timeout: 5\n--- http_config\n    upstream foo {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT dbname=test\n             password=wrong_pass user=monty protocol=mysql;\n        drizzle_keepalive mode=single max=2 overflow=reject;\n    }\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 502\n\n\n\n=== TEST 3: no database\nlittle-endian systems only\n\n--- http_config\n    upstream foo {\n        drizzle_server 127.0.0.1:1 dbname=test\n             password=some_pass user=monty protocol=mysql;\n        drizzle_keepalive mode=single max=2 overflow=reject;\n    }\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 502\n--- timeout: 1\n\n\n\n=== TEST 4: multiple queries\nlittle-endian systems only\n\n--- timeout: 5\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"select * from cats; select * from cats\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 5: missing query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 6: empty query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        set $query \"\";\n        drizzle_query $query;\n    }\n--- request\nGET /mysql\n--- error_code: 500\n--- timeout: 5\n\n\n\n=== TEST 7: empty pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend \"\";\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n\n\n=== TEST 8: empty pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $backend \"not-exist\";\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- error_code: 500\n\n"
  },
  {
    "path": "t/keepalive.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * (4 * blocks() + 12);\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=10 overflow=ignore mode=single;\n    }\n_EOC_\n\nworker_connections(128);\n#master_on();\n#log_level('warn');\n\nno_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /status {\n        drizzle_status;\n    }\n\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 5\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 2: keep-alive\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 3: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 4: select empty result\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_query \"select * from cats where name='tom'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{00}\"  # row list terminator\n--- timeout: 5\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 5: update & no module header\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 6: variables in drizzle_pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $foo backend;\n        drizzle_pass $foo;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 7: sanity (using little bufs, size 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 1;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 5\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 8: sanity (using little bufs, size 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 2;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 5\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 9: sanity (using little bufs, size 3)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 3;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 5\n--- no_error_log\n[alert]\n[error]\n\n\n\n=== TEST 10: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n--- no_error_log\n[alert]\n[error]\n\n"
  },
  {
    "path": "t/methods.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 26;\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream database {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n_EOC_\n\nworker_connections(128);\nrun_tests();\n\nno_diff();\n\n__DATA__\n\n=== TEST 1: default query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{07}\\x{00}\\x{00}\\x{00}\".  # field len\n\"default\".       # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 2: method-specific query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select 'GET' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 3: method-specific complex query (check 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 4: method-specific complex query (check 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nLOCK /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{04}\\x{00}\\x{00}\\x{00}\".  # field len\n\"LOCK\".          # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 5: method-specific complex query (using not allowed method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nHEAD /mysql\n--- error_code: 405\n--- timeout: 10\n\n\n\n=== TEST 6: method-specific query and default query (using defined method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 7: method-specific query and default query (using other method)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select 'default' as echo\";\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nPOST /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{07}\\x{00}\\x{00}\\x{00}\".  # field len\n\"default\".       # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 8: inheritance\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    drizzle_query      \"select 'default' as echo\";\n    drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n\n    location /mysql {\n        drizzle_pass       database;\n    }\n--- request\nGET /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"GET\".           # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n\n\n\n=== TEST 9: inheritance (mixed, not inherited)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    drizzle_query      \"select 'default' as echo\";\n\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      LOCK GET UNLOCK \"select '$request_method' as echo\";\n    }\n--- request\nHEAD /mysql\n--- error_code: 405\n--- timeout: 10\n\n\n\n=== TEST 10: default query\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass       database;\n        drizzle_query      \"select '$request_method' as echo\";\n    }\n--- request\nPATCH /mysql\n--- error_code: 200\n--- response_headers\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\".        # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # format version 0.0.3\n\"\\x{00}\".        # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\".  # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".              # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{01}\\x{00}\".  # col count\n\"\\x{06}\\x{80}\".  # std col type (varchar/str)\n\"\\x{fd}\\x{00}\".  # driver col type\n\"\\x{04}\\x{00}\".  # col name len\n\"echo\".          # col name data\n\"\\x{01}\".        # valid row flag\n\"\\x{05}\\x{00}\\x{00}\\x{00}\".  # field len\n\"PATCH\".         # field data\n\"\\x{00}\"         # row list terminator\n--- timeout: 10\n--- skip_nginx: 3: < 0.8.41\n"
  },
  {
    "path": "t/sanity.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n\nplan tests => repeat_each() * 2 * blocks() + 2 * repeat_each() * 6;\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nour $http_config = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        #drizzle_keepalive max=10 overflow=ignore mode=single;\n    }\n_EOC_\n\nworker_connections(128);\n#master_on();\n#log_level('warn');\n\nno_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 2: keep-alive\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 3: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 4: select empty result\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_query \"select * from cats where name='tom'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{00}\"  # row list terminator\n\n\n\n=== TEST 5: update & no module header\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 6: variables in drizzle_pass\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        set $foo backend;\n        drizzle_pass $foo;\n        drizzle_module_header off;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_headers\nX-Resty-DBD-Module: \nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n=== TEST 7: sanity (using little bufs, size 1)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 1;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 8: sanity (using little bufs, size 2)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 2;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 9: sanity (using little bufs, size 3)\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query 'select * from cats';\n        drizzle_buffer_size 3;\n    }\n--- request\nGET /mysql\n--- response_headers_like\nX-Resty-DBD-Module: ngx_drizzle \\d+\\.\\d+\\.\\d+\nContent-Type: application/x-resty-dbd-stream\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{00}\\x{00}\".  # driver errstr len\n\"\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{02}\\x{00}\".  # col count\n\"\\x{09}\\x{00}\".  # std col type (integer)\n\"\\x{03}\\x{00}\".  # drizzle col type\n\"\\x{02}\\x{00}\".     # col name len\n\"id\".   # col name data\n\"\\x{13}\\x{80}\".  # std col type (blob/str)\n\"\\x{fc}\\x{00}\".  # drizzle col type\n\"\\x{04}\\x{00}\".  # col name len\n\"name\".  # col name data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"2\".  # field data\n\"\\x{ff}\\x{ff}\\x{ff}\\x{ff}\".  # field len\n\"\".  # field data\n\"\\x{01}\".  # valid row flag\n\"\\x{01}\\x{00}\\x{00}\\x{00}\".  # field len\n\"3\".  # field data\n\"\\x{03}\\x{00}\\x{00}\\x{00}\".  # field len\n\"bob\".  # field data\n\"\\x{00}\"  # row list terminator\n--- timeout: 60\n\n\n\n=== TEST 10: update\nlittle-endian systems only\n\n--- http_config eval: $::http_config\n--- config\n    location /mysql {\n        drizzle_pass backend;\n        #drizzle_dbname $dbname;\n        drizzle_query \"update cats set name='bob' where name='bob'\";\n    }\n--- request\nGET /mysql\n--- response_body eval\n\"\\x{00}\". # endian\n\"\\x{03}\\x{00}\\x{00}\\x{00}\". # format version 0.0.3\n\"\\x{00}\". # result type\n\"\\x{00}\\x{00}\".  # std errcode\n\"\\x{00}\\x{00}\" . # driver errcode\n\"\\x{28}\\x{00}\".  # driver errstr len\n\"Rows matched: 1  Changed: 0  Warnings: 0\".  # driver errstr data\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # rows affected\n\"\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\\x{00}\".  # insert id\n\"\\x{00}\\x{00}\"  # col count\n\n\n\n"
  },
  {
    "path": "t/status.t",
    "content": "# vi:filetype=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\n#repeat_each(2);\n\nplan tests => repeat_each() * 2 * blocks();\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\n#master_on();\n\nour $http_config = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=10 overflow=reject mode=single;\n    }\n    upstream backend2 {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        #drizzle_keepalive max=10 overflow=ignore mode=single;\n    }\n    upstream foo {\n        server 127.0.0.1:80;\n    }\n_EOC_\n\nour $http_config2 = <<'_EOC_';\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=10 overflow=reject mode=single;\n    }\n    upstream backend2 {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n        drizzle_keepalive max=5 overflow=ignore mode=multi;\n    }\n_EOC_\n\nworker_connections(128);\n\nno_long_string();\n#no_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: sanity\n--- http_config eval: $::http_config\n--- config\n    location /status {\n        drizzle_status;\n    }\n--- request\n    GET /status\n--- response_body\nupstream backend\n  active connections: 0\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 0\n  free'd connection queue: 10\n  cached connection successfully used count:\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 0\n  servers: 1\n  peers: 1\n\n\n\n=== TEST 2: single mode and no pools\n--- http_config eval: $::http_config\n--- config\n    location @my_err {\n        echo \"500 Internal Server Error\";\n    }\n    location ~ ^/mysql(2?)$ {\n        drizzle_query \"select sum(1) from $args\";\n        drizzle_pass backend$1;\n        error_page 500 = @my_err;\n        rds_json on;\n    }\n    location /status {\n        drizzle_status;\n    }\n    location /main {\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo;\n\n        echo_location /status;\n\n        echo_location /mysql2 cats;\n        echo_location /mysql2 cats;\n        echo;\n\n        echo_location /status;\n    }\n--- request\n    GET /main\n--- response_body\n[{\"sum(1)\":2}][{\"sum(1)\":2}][{\"sum(1)\":2}]\nupstream backend\n  active connections: 1\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 1\n  free'd connection queue: 9\n  cached connection successfully used count: 3\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 0\n  servers: 1\n  peers: 1\n[{\"sum(1)\":2}][{\"sum(1)\":2}]\nupstream backend\n  active connections: 1\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 1\n  free'd connection queue: 9\n  cached connection successfully used count: 3\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 0\n  servers: 1\n  peers: 1\n\n\n\n=== TEST 3: single & multi mode pools\n--- http_config eval: $::http_config2\n--- config\n    location @my_err {\n        echo \"500 Internal Server Error\";\n    }\n    location ~ ^/mysql(2?)$ {\n        drizzle_query \"select sum(1) from $args\";\n        drizzle_pass backend$1;\n        error_page 500 = @my_err;\n        rds_json on;\n    }\n    location /status {\n        drizzle_status;\n    }\n    location /main {\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo;\n\n        echo_location /status;\n\n        echo_location /mysql2 cats;\n        echo_location /mysql2 cats;\n        echo;\n\n        echo_location /status;\n    }\n--- request\n    GET /main\n--- response_body\n[{\"sum(1)\":2}][{\"sum(1)\":2}][{\"sum(1)\":2}]\nupstream backend\n  active connections: 1\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 1\n  free'd connection queue: 9\n  cached connection successfully used count: 3\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 5\n  overflow: ignore\n  cached connection queue: 0\n  free'd connection queue: 5\n  cached connection successfully used count:\n  free'd connection successfully used count: 0 0 0 0 0\n  servers: 1\n  peers: 1\n[{\"sum(1)\":2}][{\"sum(1)\":2}]\nupstream backend\n  active connections: 1\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 1\n  free'd connection queue: 9\n  cached connection successfully used count: 3\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 1\n  connection pool capacity: 5\n  overflow: ignore\n  cached connection queue: 1\n  free'd connection queue: 4\n  cached connection successfully used count: 2\n  free'd connection successfully used count: 0 0 0 0\n  servers: 1\n  peers: 1\n\n\n\n=== TEST 4: single mode and bad request\n--- http_config eval: $::http_config\n--- config\n    location @my_err {\n        echo \"500 Internal Server Error\";\n    }\n    location ~ ^/mysql(2?)$ {\n        drizzle_query \"select sum(1) from $args\";\n        drizzle_pass backend$1;\n        error_page 500 = @my_err;\n        rds_json on;\n    }\n    location /status {\n        drizzle_status;\n    }\n    location /main {\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo_location /mysql select;\n        echo;\n\n        echo_location /status;\n\n        echo_location /mysql cats;\n        echo_location /mysql cats;\n        echo;\n\n        echo_location /status;\n    }\n--- request\n    GET /main\n--- response_body\n[{\"sum(1)\":2}][{\"sum(1)\":2}][{\"sum(1)\":2}]500 Internal Server Error\n\nupstream backend\n  active connections: 0\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 0\n  free'd connection queue: 10\n  cached connection successfully used count:\n  free'd connection successfully used count: 3 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 0\n  servers: 1\n  peers: 1\n[{\"sum(1)\":2}][{\"sum(1)\":2}]\nupstream backend\n  active connections: 1\n  connection pool capacity: 10\n  overflow: reject\n  cached connection queue: 1\n  free'd connection queue: 9\n  cached connection successfully used count: 2\n  free'd connection successfully used count: 0 0 0 0 0 0 0 0 0\n  servers: 1\n  peers: 1\n\nupstream backend2\n  active connections: 0\n  connection pool capacity: 0\n  servers: 1\n  peers: 1\n\n"
  },
  {
    "path": "t/timeout.t",
    "content": "# vi:ft=\n\nuse lib 'lib';\nuse Test::Nginx::Socket;\n\nrepeat_each(2);\n#repeat_each(1);\n\nplan tests => repeat_each() * (blocks() * 2 + 2);\n\nour $http_config = <<'_EOC_';\n    upstream foo {\n        drizzle_server www.taobao.com:1234;\n    }\n_EOC_\n\nworker_connections(128);\n#log_level('error');\n\n$ENV{TEST_NGINX_MYSQL_PORT} ||= 3306;\n$ENV{TEST_NGINX_MYSQL_HOST} ||= '127.0.0.1';\n\nno_diff();\n\nrun_tests();\n\n__DATA__\n\n=== TEST 1: loc_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n        drizzle_connect_timeout 10ms;\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 2: http_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    drizzle_connect_timeout 3;\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n        drizzle_connect_timeout 10ms;\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 3: serv_config connect timeout\n--- http_config eval: $::http_config\n--- config\n    drizzle_connect_timeout 10ms;\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 4: serv_config send query timeout (busy select)\n--- http_config\n    upstream backend {\n        drizzle_server 127.0.0.1:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n\n--- config\n    #drizzle_connect_timeout 1;\n    drizzle_send_query_timeout 10ms;\n    #drizzle_recv_cols_timeout 10ms;\n    #drizzle_recv_rows_timeout 10ms;\n\n    location /upstream {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query 'select sql_no_cache * from cats as a, cats as b, cats as c, cats as d, cats as e, cats as f, cats as g, cats as h, cats as i, cats as j order by a.id, b.id, c.id, d.id';\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 1\n--- SKIP\n\n\n\n=== TEST 5: loc_config connect timeout (empty $drizzle_thread_id)\n--- http_config eval: $::http_config\n--- config\n    location /upstream {\n        set $backend foo;\n        drizzle_pass $backend;\n        drizzle_module_header off;\n        drizzle_query 'select * from xx';\n        drizzle_connect_timeout 10ms;\n        more_set_headers -s 504 'X-Mysql-Tid: $drizzle_thread_id';\n    }\n--- request\nGET /upstream\n--- response_headers\nX-Mysql-Tid:\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 0.5\n\n\n\n=== TEST 6: serv_config send query timeout (sleep select)\n--- http_config\n    upstream backend {\n        drizzle_server $TEST_NGINX_MYSQL_HOST:$TEST_NGINX_MYSQL_PORT protocol=mysql\n                       dbname=ngx_test user=ngx_test password=ngx_test;\n    }\n\n--- config\n    #drizzle_connect_timeout 1;\n    drizzle_send_query_timeout 80ms;\n    #drizzle_recv_cols_timeout 10ms;\n    #drizzle_recv_rows_timeout 10ms;\n\n    location /upstream {\n        drizzle_pass backend;\n        drizzle_module_header off;\n        drizzle_query 'select sql_no_cache sleep(1);';\n        more_set_headers -s 504 \"X-Mysql-Tid: $drizzle_thread_id\";\n    }\n--- request\nGET /upstream\n--- error_code: 504\n--- response_body_like: 504 Gateway Time-out\n--- timeout: 1\n--- response_headers_like\nX-Mysql-Tid: \\d+\n\n"
  },
  {
    "path": "util/build.sh",
    "content": "#!/bin/bash\n\n# this file is mostly meant to be used by the author himself.\n\nroot=`pwd`\nversion=$1\nhome=~\nforce=$2\n\n\nngx-build $force $version \\\n            --with-ld-opt=\"-Wl,-rpath,$LIBDRIZZLE_LIB:$LUAJIT_LIB\" \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --without-mail_smtp_module \\\n            --without-http_upstream_ip_hash_module \\\n            --without-http_empty_gif_module \\\n            --without-http_memcached_module \\\n            --without-http_referer_module \\\n            --without-http_autoindex_module \\\n            --without-http_auth_basic_module \\\n            --without-http_userid_module \\\n          --add-module=$root/../echo-nginx-module \\\n          --add-module=$root/../lua-nginx-module \\\n          --add-module=$root/../rds-json-nginx-module \\\n          --add-module=$root/../headers-more-nginx-module \\\n          --add-module=$root $opts \\\n          --add-module=$root/../ndk-nginx-module \\\n          --add-module=$root/../set-misc-nginx-module \\\n          --with-select_module \\\n          --with-poll_module \\\n          --with-debug\n          #--with-cc-opt=\"-g3 -O0\"\n          #--add-module=$home/work/nginx_eval_module-1.0.1 \\\n          #--add-module=$root/../echo-nginx-module \\\n  #--without-http_ssi_module  # we cannot disable ssi because echo_location_async depends on it (i dunno why?!)\n\n"
  },
  {
    "path": "util/ngx-links",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\n\nuse Cwd qw( cwd );\nuse Getopt::Std;\n\nmy %opts;\ngetopts('f', \\%opts) or\n    die \"Usage: $0 [-f]\nOptions:\n    -f          Override exising symbolic links with force\n\";\n\nmy $root = shift || 'src';\n\nmy $force = $opts{f};\n\nopendir my $dir, $root\n    or die \"Can't open directory src/ for reading: $!\\n\";\n\nmy @links;\n\nwhile (my $entry = readdir $dir) {\n    my ($base, $ext);\n\n    my $source = \"$root/$entry\";\n\n    if (-l $source || -d $source) {\n        warn \"skipping $source\\n\";\n        next;\n    }\n\n    if ($entry =~ m{ ^ ngx_ (?: \\w+ _ )+ (\\w+) \\. ([ch]|rl) $}x) {\n        ($base, $ext) = ($1, $2);\n    } else {\n        next;\n    }\n\n    my $target = \"$root/$base.$ext\";\n    if (-e $target && ! -l $target) {\n        die \"target $target already exists, and not a symlink, not overriding...Abort.\\n\";\n    } elsif (-l $target) {\n        #warn \"it's a link\";\n        if ( ! $force ) {\n            die \"target $target already exists, not overriding...Abort.\\n\";\n        }\n        warn \"overriding existing symlink $target\\n\";\n    }\n    #warn \"creating $target --> $root/$entry\\n\";\n    system(\"ln -svf `pwd`/$source $target\") == 0 or\n        die \"Failed to create the symlink\\n\";;\n\n    push @links, $target;\n}\n\nprint join(\"\\n\", @links), \"\\n\";\n\nclose $dir;\n\n\n"
  },
  {
    "path": "util/update-readme.sh",
    "content": "#!/bin/bash\n\nperl util/wiki2pod.pl doc/manpage.wiki > /tmp/a.pod && pod2text /tmp/a.pod > README\n\n"
  },
  {
    "path": "util/wiki2pod.pl",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\nuse bytes;\n\nmy @nl_counts;\nmy $last_nl_count_level;\n\nmy @bl_counts;\nmy $last_bl_count_level;\n\nsub fmt_pos ($) {\n    (my $s = $_[0]) =~ s{\\#(.*)}{/\"$1\"};\n    $s;\n}\n\nsub fmt_mark ($$) {\n    my ($tag, $s) = @_;\n    my $max_level = 0;\n    while ($s =~ /([<>])\\1*/g) {\n        my $level = length $&;\n        if ($level > $max_level) {\n            $max_level = $level;\n        }\n    }\n\n    my $times = $max_level + 1;\n    if ($times > 1) {\n        $s = \" $s \";\n    }\n    return $tag . ('<' x $times) . $s . ('>' x $times);\n}\n\nprint \"=encoding utf-8\\n\\n\";\n\nwhile (<>) {\n    if ($. == 1) {\n        # strip the leading U+FEFF byte in MS-DOS text files\n        my $first = ord(substr($_, 0, 1));\n        #printf STDERR \"0x%x\", $first;\n        #my $second = ord(substr($_, 2, 1));\n        #printf STDERR \"0x%x\", $second;\n        if ($first == 0xEF) {\n            substr($_, 0, 1, '');\n            #warn \"Hit!\";\n        }\n    }\n    s{\\[(http[^ \\]]+) ([^\\]]*)\\]}{$2 (L<$1>)}gi;\n    s{ \\[\\[ ( [^\\]\\|]+ ) \\| ([^\\]]*) \\]\\] }{\"L<$2|\" . fmt_pos($1) . \">\"}gixe;\n    s{<code>(.*?)</code>}{fmt_mark('C', $1)}gie;\n    s{'''(.*?)'''}{fmt_mark('B', $1)}ge;\n    s{''(.*?)''}{fmt_mark('I', $1)}ge;\n    if (s{^\\s*<[^>]+>\\s*$}{}) {\n        next;\n    }\n\n    if (/^\\s*$/) {\n        print \"\\n\";\n        next;\n    }\n\n=begin cmt\n\n    if ($. == 1) {\n        warn $_;\n        for my $i (0..length($_) - 1) {\n            my $chr = substr($_, $i, 1);\n            warn \"chr ord($i): \".ord($chr).\" \\\"$chr\\\"\\n\";\n        }\n    }\n\n=end cmt\n=cut\n\n    if (/(=+) (.*) \\1$/) {\n        #warn \"HERE! $_\" if $. == 1;\n        my ($level, $title) = (length $1, $2);\n        collapse_lists();\n\n        print \"\\n=head$level $title\\n\\n\";\n    } elsif (/^(\\#+) (.*)/) {\n        my ($level, $txt) = (length($1) - 1, $2);\n        if (defined $last_nl_count_level && $level != $last_nl_count_level) {\n            print \"\\n=back\\n\\n\";\n        }\n        $last_nl_count_level = $level;\n        $nl_counts[$level] ||= 0;\n        if ($nl_counts[$level] == 0) {\n            print \"\\n=over\\n\\n\";\n        }\n        $nl_counts[$level]++;\n        print \"\\n=item $nl_counts[$level].\\n\\n\";\n        print \"$txt\\n\";\n    } elsif (/^(\\*+) (.*)/) {\n        my ($level, $txt) = (length($1) - 1, $2);\n        if (defined $last_bl_count_level && $level != $last_bl_count_level) {\n            print \"\\n=back\\n\\n\";\n        }\n        $last_bl_count_level = $level;\n        $bl_counts[$level] ||= 0;\n        if ($bl_counts[$level] == 0) {\n            print \"\\n=over\\n\\n\";\n        }\n        $bl_counts[$level]++;\n        print \"\\n=item *\\n\\n\";\n        print \"$txt\\n\";\n    } else {\n        collapse_lists();\n        print;\n    }\n}\n\ncollapse_lists();\n\nsub collapse_lists {\n    while (defined $last_nl_count_level && $last_nl_count_level >= 0) {\n        print \"\\n=back\\n\\n\";\n        $last_nl_count_level--;\n    }\n    undef $last_nl_count_level;\n    undef @nl_counts;\n\n    while (defined $last_bl_count_level && $last_bl_count_level >= 0) {\n        print \"\\n=back\\n\\n\";\n        $last_bl_count_level--;\n    }\n    undef $last_bl_count_level;\n    undef @bl_counts;\n}\n\n"
  },
  {
    "path": "valgrind.suppress",
    "content": "{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_ctl(event)\n   fun:epoll_ctl\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_hash_init\n   fun:ngx_http_variables_init_vars\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_hash_keys_array_init\n   fun:ngx_http_variables_add_core_vars\n   fun:ngx_http_core_preconfiguration\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_array_push\n   fun:ngx_hash_add_key\n   fun:ngx_http_add_variable\n   fun:ngx_http_echo_add_variables\n   fun:ngx_http_echo_handler_init\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_http_upstream_drizzle_create_srv_conf\n   fun:ngx_http_core_server\n   fun:ngx_conf_parse\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_http_upstream_drizzle_create_srv_conf\n   fun:ngx_http_upstream\n   fun:ngx_conf_parse\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_http_upstream_drizzle_create_srv_conf\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_array_push\n   fun:ngx_hash_add_key\n   fun:ngx_http_variables_add_core_vars\n   fun:ngx_http_core_preconfiguration\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_hash_init\n   fun:ngx_http_upstream_init_main_conf\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   nginx-core-process-init\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_event_process_init\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   nginx-core-crc32-init\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_crc32_table_init\n   fun:main\n}\n{\n   palloc_large_for_init_request\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_pcalloc\n   fun:ngx_http_init_request\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   palloc_large_for_create_temp_buf\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_create_temp_buf\n   fun:ngx_http_init_request\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   accept_create_pool\n   Memcheck:Leak\n   fun:memalign\n   fun:posix_memalign\n   fun:ngx_memalign\n   fun:ngx_create_pool\n   fun:ngx_event_accept\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   create_pool_for_init_req\n   Memcheck:Leak\n   fun:memalign\n   fun:posix_memalign\n   fun:ngx_memalign\n   fun:ngx_create_pool\n   fun:ngx_http_init_request\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n   fun:ngx_single_process_cycle\n   fun:main\n}\n{\n   create_pool_posix_memalign\n   Memcheck:Leak\n   fun:memalign\n   fun:posix_memalign\n   fun:ngx_memalign\n   fun:ngx_create_pool\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_palloc_large\n   fun:ngx_palloc\n   fun:ngx_array_push\n   fun:ngx_hash_add_key\n   fun:ngx_http_add_variable\n   fun:ngx_http_ssi_preconfiguration\n   fun:ngx_http_block\n   fun:ngx_conf_parse\n   fun:ngx_init_cycle\n   fun:main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Cond\n   fun:index\n   fun:expand_dynamic_string_token\n   fun:_dl_map_object\n   fun:map_doit\n   fun:_dl_catch_error\n   fun:do_preload\n   fun:dl_main\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_set_environment\n   fun:ngx_single_process_cycle\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Leak\n   match-leak-kinds: definite\n   fun:malloc\n   fun:ngx_alloc\n   fun:ngx_set_environment\n   fun:ngx_worker_process_init\n   fun:ngx_worker_process_cycle\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:epoll_wait\n   fun:ngx_epoll_process_events\n   fun:ngx_process_events_and_timers\n}\n{\n   <insert_a_suppression_name_here>\n   Memcheck:Param\n   epoll_pwait(sigmask)\n   fun:epoll_pwait\n   fun:epoll_wait\n   fun:ngx_epoll_test_rdhup\n   fun:ngx_epoll_init\n   fun:ngx_event_process_init\n}\n{\n    <insert_a_suppression_name_here>\n    Memcheck:Leak\n    match-leak-kinds: definite\n    fun:malloc\n    fun:ngx_alloc\n    fun:ngx_event_process_init\n    fun:ngx_worker_process_init\n}\n"
  }
]