pax_global_header00006660000000000000000000000064124703747550014530gustar00rootroot0000000000000052 comment=a762a5b0a94413289ccc386a7ea823aa8a8ca022 pgmemcache-2.3.0/000077500000000000000000000000001247037475500136235ustar00rootroot00000000000000pgmemcache-2.3.0/.gitignore000066400000000000000000000001231247037475500156070ustar00rootroot00000000000000.coverity-email .coverity-token *~ *.o *.so /pgmemcache.control /pgmemcache--*.sql pgmemcache-2.3.0/.travis.yml000066400000000000000000000011421247037475500157320ustar00rootroot00000000000000language: c compiler: gcc install: - sudo apt-get install memcached postgresql-server-dev-9.1 postgresql-server-dev-9.2 postgresql-server-dev-9.3 script: - make clean - make PG_CONFIG=/usr/lib/postgresql/9.1/bin/pg_config - PATH=/usr/lib/postgresql/9.1/bin/:$PATH ./localtests.sh - make clean - make PG_CONFIG=/usr/lib/postgresql/9.2/bin/pg_config - PATH=/usr/lib/postgresql/9.2/bin/:$PATH ./localtests.sh - make clean - make PG_CONFIG=/usr/lib/postgresql/9.3/bin/pg_config - PATH=/usr/lib/postgresql/9.3/bin/:$PATH ./localtests.sh after_failure: - cat regressiondata/regression.diffs pgmemcache-2.3.0/LICENSE000066400000000000000000000024641247037475500146360ustar00rootroot00000000000000Copyright (c) 2004-2005 Sean Chittenden Copyright (c) 2007-2008 Neil Conway Copyright (c) 2007 Open Technology Group, Inc. Copyright (c) 2008-2013 Hannu Valtonen Copyright (c) 2012-2014 Ohmu Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pgmemcache-2.3.0/Makefile000066400000000000000000000041201247037475500152600ustar00rootroot00000000000000short_ver = 2.3.0 long_ver = $(shell git describe --long 2>/dev/null || echo $(short_ver)-0-unknown) MODULE_big = pgmemcache OBJS = pgmemcache.o EXTENSION = pgmemcache DATA_built = pgmemcache--$(short_ver).sql pgmemcache.control DATA = ext/pgmemcache--unpackaged--2.0.sql \ ext/pgmemcache--2.0--2.1.sql \ ext/pgmemcache--2.1--2.1.1.sql \ ext/pgmemcache--2.1.1--2.1.2.sql \ ext/pgmemcache--2.1.2--2.2.0.sql \ ext/pgmemcache--2.2.0--2.3.0.sql REGRESS = init start_memcached test stop_memcached ifeq ($(USE_OMCACHE),1) SHLIB_LINK = -lomcache PG_CPPFLAGS += -DUSE_OMCACHE else SHLIB_LINK = -lmemcached -lsasl2 PG_CPPFLAGS += -DUSE_LIBMEMCACHED endif PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pgmemcache.control: ext/pgmemcache.control sed -e 's,__short_ver__,$(short_ver),g' < $^ > $@ pgmemcache--$(short_ver).sql: ext/pgmemcache.sql cp -fp $^ $@ dist: git archive --output=../pgmemcache_$(long_ver).tar.gz --prefix=pgmemcache/ HEAD . deb%: cp debian/changelog.in debian/changelog dch -v $(long_ver) "Automatically built package" sed -e s/PGVERSION/$(subst deb,,$@)/g < debian/control.in > debian/control echo $(subst deb,,$@) > debian/pgversions PGVERSION=$(subst deb,,$@) dpkg-buildpackage -uc -us rpm: git archive --output=pgmemcache-rpm-src.tar.gz --prefix=pgmemcache/ HEAD rpmbuild -ta pgmemcache-rpm-src.tar.gz \ --define 'full_version $(long_ver)' \ --define 'major_version $(short_ver)' \ --define 'minor_version $(subst -,.,$(subst $(short_ver)-,,$(long_ver)))' $(RM) pgmemcache-rpm-src.tar.gz build-dep: apt-get install libmemcached-dev postgresql-server-dev libpq-dev devscripts libsasl2-dev check-coverity: $(MAKE) clean $(RM) -r cov-int pgmemcache-cov-int.tar.gz cov-build --dir cov-int $(MAKE) tar zcvf pgmemcache-cov-int.tar.gz cov-int curl --verbose --form 'token=<.coverity-token' \ --form 'email=<.coverity-email' \ --form 'file=@pgmemcache-cov-int.tar.gz' \ --form 'version=$(long_ver)' \ --form 'description=$(short_ver)' \ 'https://scan.coverity.com/builds?project=ohmu%2Fpgmemcache' $(RM) -r cov-int pgmemcache-cov-int.tar.gz pgmemcache-2.3.0/NEWS000066400000000000000000000220531247037475500143240ustar00rootroot00000000000000pgmemcache 2.3.0 (2015-02-16) ============================= * Preliminary support for using OMcache instead of libmemcached for communicating with memcached servers * Continuous integration at travis-ci.org * Consistent and fixed text argument handling * Fix behavior parsing to compare full behavior names instead of prefixes * Flush client buffers to memcache servers on commit if the new GUC pgmemcache.flush_on_commit is enabled, otherwise some buffers may get lost when the PostgreSQL backend terminates before everything has been sent which is especially likely when the BUFFER_REQUESTS behavior is used * Return NULL when a set-related function returns MEMCACHED_BUFFERED to indicate that we don't really know the result of the action, but it wasn't an error pgmemcache 2.2.0 (2014-09-09) ============================= * Support for MEMCACHED_BEHAVIOR_DEAD_TIMEOUT with libmemcached >= 1.0.3 * Changing default_servers now drops old servers from the list * debian: new packaging to replace yada which is not available in Debian Wheezy anymore (Oskari Saarenmaa) * Use pg_regress for make installcheck testing (Christoph Berg) * Fix memory allocation (Mika Eloranta, Oskari Saarenmaa) pgmemcache 2.1.2 (2013-11-12) ============================= * memcache_get_multi crash fixes (Maxim Kuzevanov) * RPM packaging support * Fix versioning when building without git (reported by Patryk Kordylewski) * Code refactoring with minor fixes and consistent indentation and style pgmemcache 2.1.1 (2013-05-15) ============================= * Fix errors and warnings exposed by GCC 4.8 * Make the memcache binary protocol the default * Change memcache_incr and memcache_decr to use BIGINT arguments for the increment/decrement values (patch from Markus Holmberg / F-Secure) pgmemcache 2.1.0 (2013-04-22) ============================= * CREATE EXTENSION support * PostgreSQL 9.2 support * Fix crashers on NULL inputs * Fix a random initialization failure on startup, this fixes errors such as: """WARNING: invalid value for parameter "pgmemcache.default_servers": "example.org" ERROR: NO SERVERS DEFINED""" * Compatibility with newer libmemcached versions * Updated Debian packaging scripts pgmemcache 2.0.6 (2011-04-25) ============================= * Fix building on earlier releases than 9.1 (reported by Florian Von Block) * Add libsasl2-dev to build deps pgmemcache 2.0.5 (2011-04-24) ============================= * Some preliminary work on SASL support * Make pgmemcache work with PG 9.1 * Support for building debs against PG 9.0 and 9.1 * Move to GIT. * Fix erroneous memory allocation get_multi (thanks iglue) pgmemcache 2.0.4 ================ * Add support for building an HTML doc out of the README.pgmemcache * Make pgmemcache work with libmemcached's 0.39 and 0.40 which again break API compatibility * Remove memcache_stats(TEXT) (Note, memcache_stats() is still supported) pgmemcache 2.0.3 ================ * Add support for all libmemcached 0.38 behaviors (a few had been added since these were updated the last time) * Add preliminary Debian packaging support, documentation was updated a bit to reflect this pgmemcache 2.0.2 ================ * Make pgmemcache work with libmemcached 0.38 memory allocators * This bumps up pgmemcache's libmemcached requirement up to 0.38 pgmemcache 2.0.1 ================ * Remove some deprecated documentation * Fix bug "[#1010761] Crash with pgmemcache 2.0.0 and postgres 8.4", thanks to Marc Munro for pointing out the problem and a fix for it * Make libmemcached use PostgreSQL memory allocation routines. (requires libmemcached 0.31+), based on a patch from Peter Meszaros with some editorialization by Hannu Valtonen * Note this bumps up our libmemcached minimum requirement to 0.31 pgmemcache 2.0.0 ================ Finally a 2.0.0 release. * Fix default (unset) GUC crashes, based on a patch from Claes Jakobsson * Add BYTEA memcache_get * Add TEXT[], BYTEA[] memcache_get_multi for the reduction of server roundtrips pgmemcache 2.0 RC 1 =================== It looks likely that this is the last release before stamping this as 2.0. * Fix pgmemcache not to throw an error when trying to delete a non-existent key from memcache (based on a report from Chander Ganesan) pgmemcache 2.0 beta2 ==================== On the way towards a 2.0 release: * Make pgmemcache work on top of older versions of PostgreSQL (at least 8.3, probably earlier) * Make libmemcached behavior configurable through PostgreSQL GUCs * prepend/append support pgmemcache 2.0 beta1 ==================== pgmemcache was completely rewritten to work on top of libmemcached. It requires PostgreSQL 8.4+ and is not backwards compatible with older versions of PostgreSQL. That may or may not change depending on how much are people interested in having it work on older versions. Also some API's were deprecated: * memcache_server_find_hash * memcache_flush (note, not flush all) * memcache_server_list (use stats) * memcache_server_remove * memcache_hash Also starting from 2.0 beta 1 the maintainership has moved to Hannu Valtonen. pgmemcache 1.2 beta1 ==================== This release has a lot of new code and backward-incompatible changes; it might be more accurate to label it "2.0". * Add the pgmemcache.default_servers GUC variable. This specifies a list of memcached servers ('host:port' pairs) that pgmemcache will connect to by default. To use this, add pgmemcache to preload_shared_libraries and custom_variable_classes, and then define pgmemcache.default_servers as desired, all in postgreql.conf * Remove memcache_free() and memcache_init(). Instead, define _PG_init() and _PG_fini() functions, so that Postgres can invoke them to do the appropriate initialization/cleanup work when pgmemcache is loaded/unloaded from a process. * Don't allocate any memory in TopMemoryContext directly. Instead, create a small, long-lived context as a child of TopMemoryContext and use that for the allocations we need to make. * Add a new function, memcache_server_remove(). * Add a new SRF, memcache_server_list(). * Fix compile breakage against recent CVS HEAD. * Change memcache_server_find(TEXT) and memcache_server_find(INT4) to use OUT parameters, rather than returning a 'host:port' pair as a single text value. * Fix for crashes in memcache_incr(), memcache_decr(), memcache_hash(): these weren't prepared to handle NULL inputs. * Fix crash bug in memcache_flush_all0(): this function tried to access a nonexistent function argument. * Remove support for memcache_add(TEXT), memcache_set(TEXT), and memcache_replace(TEXT). These were pointless, and treated NULL as the empty string, which is wrong. * Remove support for the "flags" concept from the API. This wasn't actually feature complete (there was no way to fetch a flags value), and was buggy anyway. I might readd this later. * Removed deprecated function memcache_flush_all(TEXT); memcache_flush(TEXT) should be used instead. * Various code cleanup, editorialization on error message formats, and refactorings. pgmemcache 1.1 ============== * Add a TODO list. * Fix a potential memory leak in memcache_server_add(): this function might have unwittingly allocated memory in TopMemoryContext. * Make the "port" argument to memcache_server_add() optional. If not specified, it defaults to the libmemcache default (11211). * Add a "dist" target to the Makefile. pgmemcache 1.1 beta1 ==================== * New maintainer: Neil Conway. Thanks to the sponsorship of The Open Technology Group. * Change build system to use just a normal Makefile and the Postgres PGXS infrastructure, rather than pmk. This means pmk is no longer a build dependency. * Various fixes to allow pgmemcache to be built against PostgreSQL 8.2, including adding PG_MODULE_MAGIC. I've briefly tested this release against CVS HEAD, 8.2, and 8.1. Note that this release will NOT compile against PostgreSQL 8.0 or earlier; if this is important to people, this could be fixed pretty easily. * Fix a logic error in memcache_set_cmd(): as a result, memcache_set() and memcache_replace() now work as intended, instead of being aliases for memcache_add(). * Fix a buffer overflow in memcache_gen_host(): this function neglected to allocate space for the varlena header. * Fix a read of uninitialized memory in memcache_atomic_op(), memcache_delete() and memcache_set_cmd(). * Remove all the code that connected and disconnected from SPI. As far as I can see, pgmemcache has no need to use SPI at all: SPI is intended for issuing SQL queries, which pgmemcache has no need to do. Similarly, use palloc() rather than SPI_palloc(). * Fix various compiler warnings with gcc -Wall on AMD64, and presumably other platforms as well. Use the C99 "PRIu64" macro to get a portable printf(3) conversion specifier for 64-bit unsigned integers. * Fix error message style for elog() message strings: error message should not begin with a capital letter. * Optimize a few functions to use a stack-allocated StringInfoData, rather than a heap-allocated StringInfo. * Fix typo in the implementation of memcache_stat(TEXT). pgmemcache-2.3.0/README.rst000066400000000000000000000201161247037475500153120ustar00rootroot00000000000000pgmemcache |BuildStatus|_ ========================= .. |BuildStatus| image:: https://travis-ci.org/ohmu/pgmemcache.png?branch=master .. _BuildStatus: https://travis-ci.org/ohmu/pgmemcache pgmemcache is a set of PostgreSQL user-defined functions that provide an interface to memcached. Installing pgmemcache is easy, but does have a few trivial requirements. Requirements ============ * PostgreSQL_ 9.1 or newer * libmemcached_ 0.38 or OMcache_ 0.2.0 or newer .. _PostgreSQL: http://www.postgresql.org/ .. _libmemcached: http://libmemcached.org/ .. _OMcache: https://github.com/ohmu/omcache Always prefer the latest versions. pgmemcache uses libmemcached by default, but support for an alternative memcache client library, OMcache, was added during pgmemcache 2.2.x development. pgmemcache can be built with OMcache instead of libmemcached by passing USE_OMCACHE=1 argument to make. pgmemcache uses the memcache binary protocol by default, this is required for the "increment / decrement with initial" operations pgmemcache uses. Installation ============ Building from sources --------------------- 1) Install PostgreSQL with PGXS (if not already installed) 2) Install libmemcached_ or OMcache_ 3) make 4) sudo make install If necessary, update LD_LIBRARY_CACHE for the PostgreSQL server owner and restart PostgreSQL. Also make sure that the pg_config binary is in your PATH (or edit the Makefile). Building on Debian ------------------ 1) sudo make build-dep 2) make deb9.2 (or deb9.3, deb9.4, etc depending on your PostgreSQL version) 3) sudo dpkg -i ../postgresql-pgmemcache-*.deb Building on Fedora, CentOS or RHEL ---------------------------------- 1) make rpm 2) sudo rpm -Uvh $(rpm --eval '%{_rpmdir}/%{_arch}')/pgmemcache-*.rpm Setup for PostgreSQL ==================== :: % psql -c 'create extension pgmemcache' It is often more convenient to specify a list of memcached servers to connect to in postgresql.conf, rather than calling memcache_server_add() in each new client connection. This can be done as follows: 1. Edit postgresql.conf 2. Append "pgmemcache" to shared_preload_libraries (If using PostgreSQL 9.1 or earlier versions) 3. Append "pgmemcache" to custom_variable_classes (Optional parts) 4. Set the "pgmemcache.default_servers" custom GUC variable to a comma-separated list of 'host:port' pairs (the port is optional). 5. Set the pgmemcache.default_behavior flags to suit your needs. The format is a comma-separated list of memcached behavior (optional) of behavior_flag:behavior_data. The flags correspond with libmemcached behavior flags. Check the libmemcached documentation for those. As an example behavior you might have the following line in your postgresql.conf:: pgmemcache.default_behavior='DEAD_TIMEOUT:2' In case your system has SELinux please install the required SELinux policy:: /usr/bin/checkmodule -M -m -o pgmemcache.mod pgmemcache.te /usr/bin/semodule_package -o pgmemcache.pp -m pgmemcache.mod /usr/sbin/semodule -i pgmemcache.pp Usage ===== :: memcache_server_add('hostname:port'::TEXT) memcache_server_add('hostname'::TEXT) Adds a server to the list of available servers. If the port is not specified, the memcached default port (11211) is used. This should only be done in one central place in the code (normally wrapped in an IF statement). :: memcache_add(key::TEXT, value::TEXT, expire::TIMESTAMPTZ) memcache_add(key::TEXT, value::TEXT, expire::INTERVAL) memcache_add(key::TEXT, value::TEXT) Adds a key to the cache cluster, if the key does not already exist. :: newval = memcache_decr(key::TEXT, decrement::INT8) newval = memcache_decr(key::TEXT) If key exists and is an integer, atomically decrements by the value specified (default decrement is one). Returns INT value after decrement. :: memcache_delete(key::TEXT, hold_timer::INTERVAL) memcache_delete(key::TEXT) Deletes a given key. If a hold timer is specified, key with the same name can't be added until the hold timer expires. :: memcache_flush_all() Flushes (drops) all data on all servers in the memcache cluster. :: value = memcache_get(key::TEXT) Fetches a key out of the cache. Returns NULL if the key does not exist; otherwise, it returns the value of the key as TEXT. Note that zero-length values are allowed. :: memcache_get_multi(keys::TEXT[]) memcache_get_multi(keys::BYTEA[]) SELECT key, value FROM memcache_get_multi('{qwerty,asdfg}'::TEXT[]); Fetches an ARRAY of keys from the cache, returns a list of RECORDs for the found keys, with the columns titled key and value. :: newval = memcache_incr(key::TEXT, increment::INT8) newval = memcache_incr(key::TEXT) If key exists and is an integer, atomically increment by the value specified (the default increment is one). Returns INT value after increment. :: memcache_replace(key::TEXT, value::TEXT, expire::TIMESTAMPTZ) memcache_replace(key::TEXT, value::TEXT, expire::INTERVAL) memcache_replace(key::TEXT, value::TEXT) Replaces an existing key's value if the key already exists. :: memcache_set(key::TEXT, value::TEXT, expire::TIMESTAMPTZ) memcache_set(key::TEXT, value::TEXT, expire::INTERVAL) memcache_set(key::TEXT, value::TEXT) Regardless of whether the specified key already exists, set its current value to "value", replacing the previous value if any. :: stats = memcache_stats() Returns a TEXT string with all of the stats from all servers in the server list. Examples ======== Most installations will need a few functions to allow pgmemcache to work correctly. Here are a few example functions that should get most people off the ground and running. The following function is an example of a trigger function that is used to replace the value of something in the cache with its new value. :: CREATE OR REPLACE FUNCTION auth_passwd_upd() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF OLD.passwd <> NEW.passwd THEN PERFORM memcache_replace('user_id_' || NEW.user_id || '_password', NEW.passwd); END IF; RETURN NEW; END; $$; Activate the trigger for UPDATEs:: CREATE TRIGGER auth_passwd_upd_trg AFTER UPDATE ON passwd FOR EACH ROW EXECUTE PROCEDURE auth_passwd_upd(); The above is not transaction safe, however. A better approach is to have pgmemcache invalidate the cached data, but not replace it. :: CREATE OR REPLACE FUNCTION auth_passwd_upd() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN IF OLD.passwd <> NEW.passwd THEN PERFORM memcache_delete('user_id_' || NEW.user_id || '_password'); END IF; RETURN NEW; END; $$; Here's an example delete trigger:: CREATE OR REPLACE FUNCTION auth_passwd_del() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN PERFORM memcache_delete('user_id_' || OLD.user_id || '_password'); RETURN OLD; END; $$; Activate the trigger for DELETEs:: CREATE TRIGGER auth_passwd_del_trg AFTER DELETE ON passwd FOR EACH ROW EXECUTE PROCEDURE auth_passwd_del(); License ======= pgmemcache is released under an MIT-style license (BSD without advertising clause). For the exact license terms, see the file "LICENSE". Contact ======= pgmemcache is currently maintained by Hannu Valtonen and Oskari Saarenmaa from Ohmu Ltd, they can be contacted at . Bug reports and patches are very welcome; issues should be reported in GitHub's issue interface (https://github.com/ohmu/pgmemcache) and patches and other enhancement proposals should be submitted as GitHub pull requests. Credits ======= pgmemcache was originally written by Sean Chittenden. Version 1.x series was maintained by Neil Conway and sponsored by the Open Technology Group, Inc. Version 2.0 was rewritten to work on top of libmemcached and the maintainership moved to Hannu Valtonen. Suzuki Hironobu contributed major patches for the 2.0 series, among other things, support for libmemcached configuration settings. F-Secure Corporation contributed extension support and bug fixes for version 2.1. See https://github.com/ohmu/pgmemcache/graphs/contributors for the list of recent contributors. pgmemcache-2.3.0/TODO000066400000000000000000000000651247037475500143140ustar00rootroot00000000000000TODO: ===== * finish SASL support * SQL/MED support? pgmemcache-2.3.0/debian/000077500000000000000000000000001247037475500150455ustar00rootroot00000000000000pgmemcache-2.3.0/debian/.gitignore000066400000000000000000000001251247037475500170330ustar00rootroot00000000000000/*.log /*.substvars /changelog /control /files /pgversions /postgresql-pgmemcache-*/ pgmemcache-2.3.0/debian/changelog.in000066400000000000000000000012211247037475500173200ustar00rootroot00000000000000pgmemcache (2.1.1) unstable; urgency=low * v2.1.1 -- Oskari Saarenmaa Tue, 14 May 2013 15:02:04 +0300 pgmemcache (2.0.6) unstable; urgency=low * v2.0.6 -- Hannu Valtonen Wed, 25 Apr 2011 23:09:00 +0200 pgmemcache (2.0.5) unstable; urgency=low * v2.0.5 -- Hannu Valtonen Wed, 24 Apr 2011 13:31:00 +0200 pgmemcache (2.0.4) unstable; urgency=low * v2.0.4 -- Hannu Valtonen Wed, 05 May 2010 22:56:00 +0200 pgmemcache (2.0.3) unstable; urgency=low * v2.0.3 -- Hannu Valtonen Sat, 02 Apr 2010 13:00:00 +0200 pgmemcache-2.3.0/debian/compat000066400000000000000000000000021247037475500162430ustar00rootroot000000000000008 pgmemcache-2.3.0/debian/control.in000066400000000000000000000010511247037475500170520ustar00rootroot00000000000000Source: pgmemcache Section: database Priority: extra Maintainer: Ohmu Opensource Build-Depends: debhelper (>= 8), postgresql-server-dev-PGVERSION, libmemcached-dev (>= 0.38~), libsasl2-dev Standards-Version: 3.9.5 Homepage: https://github.com/ohmu/pgmemcache/ Package: postgresql-pgmemcache-PGVERSION Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-PGVERSION Description: PostgreSQL interface to memcached pgmemcache is a set of PostgreSQL user-defined functions that provide an interface to memcached. pgmemcache-2.3.0/debian/copyright000066400000000000000000000026761247037475500170130ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: pgmemcache Source: https://github.com/ohmu/pgmemcache/ Files: * Copyright: 2004-2005 Sean Chittenden 2007-2008 Neil Conway 2007 Open Technology Group, Inc. 2008-2013 Hannu Valtonen 2012-2014 Ohmu Ltd License: MIT/X Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pgmemcache-2.3.0/debian/rules000077500000000000000000000004741247037475500161320ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export PATH := /usr/lib/postgresql/$(PGVERSION)/bin:$(PATH) override_dh_installchangelogs: dh_installchangelogs NEWS override_dh_installdocs: dh_installdocs --all README.rst %: dh $@ .PHONY: debian/control pgmemcache-2.3.0/expected/000077500000000000000000000000001247037475500154245ustar00rootroot00000000000000pgmemcache-2.3.0/expected/init.out000066400000000000000000000000351247037475500171160ustar00rootroot00000000000000CREATE EXTENSION pgmemcache; pgmemcache-2.3.0/expected/start_memcached.out000066400000000000000000000000741247037475500213010ustar00rootroot00000000000000\! memcached -p 33211 -P $HOME/.pgmemcache-memcached.pid -d pgmemcache-2.3.0/expected/stop_memcached.out000066400000000000000000000000561247037475500211310ustar00rootroot00000000000000\! kill `cat $HOME/.pgmemcache-memcached.pid` pgmemcache-2.3.0/expected/test.out000066400000000000000000000017751247037475500171460ustar00rootroot00000000000000\i test.sql SELECT memcache_server_add('localhost:33211'); memcache_server_add --------------------- t (1 row) SELECT regexp_replace(memcache_stats(), 'pid:.*', '') AS memcache_stats; memcache_stats --------------------------- Server: localhost (33211)+ (1 row) SELECT memcache_delete('jeah'); memcache_delete ----------------- f (1 row) SELECT memcache_set('jeah','test_value1'); memcache_set -------------- t (1 row) SELECT memcache_add('counter','10'); memcache_add -------------- t (1 row) SELECT memcache_incr('counter', 30); memcache_incr --------------- 40 (1 row) SELECT memcache_get('counter'); memcache_get -------------- 40 (1 row) SELECT memcache_decr('counter'); memcache_decr --------------- 39 (1 row) SELECT memcache_delete('counter'); memcache_delete ----------------- t (1 row) SELECT memcache_get('counter'); memcache_get -------------- (1 row) SELECT memcache_get('jeah'); memcache_get -------------- test_value1 (1 row) pgmemcache-2.3.0/ext/000077500000000000000000000000001247037475500144235ustar00rootroot00000000000000pgmemcache-2.3.0/ext/pgmemcache--2.0--2.1.sql000066400000000000000000000031601247037475500201620ustar00rootroot00000000000000\echo Use "ALTER EXTENSION pgmemcache UPDATE TO '2.1'" to load this file. \quit ALTER FUNCTION memcache_server_add(text) STRICT; ALTER FUNCTION memcache_stats() STRICT; ALTER FUNCTION memcache_flush_all() STRICT; ALTER FUNCTION memcache_delete(text, interval) STRICT; ALTER FUNCTION memcache_delete(text) STRICT; ALTER FUNCTION memcache_get(text) STRICT; ALTER FUNCTION memcache_get(bytea) STRICT; ALTER FUNCTION memcache_get_multi(text[]) STRICT; ALTER FUNCTION memcache_get_multi(bytea[]) STRICT; ALTER FUNCTION memcache_set(text, text, timestamptz) STRICT; ALTER FUNCTION memcache_set(text, text, interval) STRICT; ALTER FUNCTION memcache_set(text, text) STRICT; ALTER FUNCTION memcache_set(bytea, text) STRICT; ALTER FUNCTION memcache_set(text, bytea) STRICT; ALTER FUNCTION memcache_add(text, text, timestamptz) STRICT; ALTER FUNCTION memcache_add(text, text, interval) STRICT; ALTER FUNCTION memcache_add(text, text) STRICT; ALTER FUNCTION memcache_replace(text, text, timestamptz) STRICT; ALTER FUNCTION memcache_replace(text, text, interval) STRICT; ALTER FUNCTION memcache_replace(text, text) STRICT; ALTER FUNCTION memcache_append(text, text, timestamptz) STRICT; ALTER FUNCTION memcache_append(text, text, interval) STRICT; ALTER FUNCTION memcache_append(text, text) STRICT; ALTER FUNCTION memcache_prepend(text, text, timestamptz) STRICT; ALTER FUNCTION memcache_prepend(text, text, interval) STRICT; ALTER FUNCTION memcache_prepend(text, text) STRICT; ALTER FUNCTION memcache_incr(text, int) STRICT; ALTER FUNCTION memcache_incr(text) STRICT; ALTER FUNCTION memcache_decr(text, int) STRICT; ALTER FUNCTION memcache_decr(text) STRICT; pgmemcache-2.3.0/ext/pgmemcache--2.1--2.1.1.sql000066400000000000000000000013641247037475500203260ustar00rootroot00000000000000\echo Use "ALTER EXTENSION pgmemcache UPDATE TO '2.1.1'" to load this file. \quit DROP FUNCTION memcache_incr(key text, increment int); CREATE FUNCTION memcache_incr(key text, increment bigint) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_incr' LANGUAGE c STRICT; DROP FUNCTION memcache_incr(key text); CREATE FUNCTION memcache_incr(key text) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_incr' LANGUAGE c STRICT; DROP FUNCTION memcache_decr(key text, decrement int); CREATE FUNCTION memcache_decr(key text, decrement bigint) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_decr' LANGUAGE c STRICT; DROP FUNCTION memcache_decr(key text); CREATE FUNCTION memcache_decr(key text) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_decr' LANGUAGE c STRICT; pgmemcache-2.3.0/ext/pgmemcache--2.1.1--2.1.2.sql000066400000000000000000000001071247037475500204600ustar00rootroot00000000000000\echo Use "ALTER EXTENSION pgmemcache UPDATE" to load this file. \quit pgmemcache-2.3.0/ext/pgmemcache--2.1.2--2.2.0.sql000066400000000000000000000001071247037475500204600ustar00rootroot00000000000000\echo Use "ALTER EXTENSION pgmemcache UPDATE" to load this file. \quit pgmemcache-2.3.0/ext/pgmemcache--2.2.0--2.3.0.sql000066400000000000000000000001071247037475500204600ustar00rootroot00000000000000\echo Use "ALTER EXTENSION pgmemcache UPDATE" to load this file. \quit pgmemcache-2.3.0/ext/pgmemcache--unpackaged--2.0.sql000066400000000000000000000050531247037475500217670ustar00rootroot00000000000000-- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pgmemcache" to load this file. \quit ALTER EXTENSION pgmemcache ADD FUNCTION memcache_server_add(server_hostname text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_stats(); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_flush_all(); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_delete(key text, hold interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_delete(key text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_get(key text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_get(key bytea); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_get_multi(IN keys text[], OUT key text, OUT value text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_get_multi(IN keys bytea[], OUT key text, OUT value text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_set(key text, val text, expire timestamptz); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_set(key text, val text, expire interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_set(key text, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_set(key bytea, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_set(key text, val bytea); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_add(key text, val text, expire timestamptz); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_add(key text, val text, expire interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_add(key text, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_replace(key text, val text, expire timestamptz); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_replace(key text, val text, expire interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_replace(key text, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_append(key text, val text, expire timestamptz); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_append(key text, val text, expire interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_append(key text, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_prepend(key text, val text, expire timestamptz); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_prepend(key text, val text, expire interval); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_prepend(key text, val text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_incr(key text, increment int); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_incr(key text); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_decr(key text, decrement int); ALTER EXTENSION pgmemcache ADD FUNCTION memcache_decr(key text); pgmemcache-2.3.0/ext/pgmemcache.control000066400000000000000000000002231247037475500201130ustar00rootroot00000000000000# pgmemcache extension comment = 'memcached interface' default_version = '__short_ver__' module_pathname = '$libdir/pgmemcache' relocatable = true pgmemcache-2.3.0/ext/pgmemcache.sql000066400000000000000000000100071247037475500172330ustar00rootroot00000000000000-- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pgmemcache" to load this file. \quit CREATE FUNCTION memcache_server_add(server_hostname text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_server_add' LANGUAGE c STRICT; CREATE FUNCTION memcache_stats() RETURNS text AS 'MODULE_PATHNAME', 'memcache_stats' LANGUAGE c STRICT; CREATE FUNCTION memcache_flush_all() RETURNS bool AS 'MODULE_PATHNAME', 'memcache_flush_all0' LANGUAGE c STRICT; CREATE FUNCTION memcache_delete(key text, hold interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_delete' LANGUAGE c STRICT; CREATE FUNCTION memcache_delete(key text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_delete' LANGUAGE c STRICT; CREATE FUNCTION memcache_get(key text) RETURNS text AS 'MODULE_PATHNAME', 'memcache_get' LANGUAGE c STRICT; CREATE FUNCTION memcache_get(key bytea) RETURNS text AS 'MODULE_PATHNAME', 'memcache_get' LANGUAGE c STRICT; CREATE FUNCTION memcache_get_multi(IN keys text[], OUT key text, OUT value text) RETURNS SETOF record AS 'MODULE_PATHNAME', 'memcache_get_multi' LANGUAGE c STRICT; CREATE FUNCTION memcache_get_multi(IN keys bytea[], OUT key text, OUT value text) RETURNS SETOF record AS 'MODULE_PATHNAME', 'memcache_get_multi' LANGUAGE c STRICT; CREATE FUNCTION memcache_set(key text, val text, expire timestamptz) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_set_absexpire' LANGUAGE c STRICT; CREATE FUNCTION memcache_set(key text, val text, expire interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_set' LANGUAGE c STRICT; CREATE FUNCTION memcache_set(key text, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_set' LANGUAGE c STRICT; CREATE FUNCTION memcache_set(key bytea, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_set' LANGUAGE c STRICT; CREATE FUNCTION memcache_set(key text, val bytea) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_set' LANGUAGE c STRICT; CREATE FUNCTION memcache_add(key text, val text, expire timestamptz) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_add_absexpire' LANGUAGE c STRICT; CREATE FUNCTION memcache_add(key text, val text, expire interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_add' LANGUAGE c STRICT; CREATE FUNCTION memcache_add(key text, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_add' LANGUAGE c STRICT; CREATE FUNCTION memcache_replace(key text, val text, expire timestamptz) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_replace_absexpire' LANGUAGE c STRICT; CREATE FUNCTION memcache_replace(key text, val text, expire interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_replace' LANGUAGE c STRICT; CREATE FUNCTION memcache_replace(key text, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_replace' LANGUAGE c STRICT; CREATE FUNCTION memcache_append(key text, val text, expire timestamptz) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_append_absexpire' LANGUAGE c STRICT; CREATE FUNCTION memcache_append(key text, val text, expire interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_append' LANGUAGE c STRICT; CREATE FUNCTION memcache_append(key text, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_append' LANGUAGE c STRICT; CREATE FUNCTION memcache_prepend(key text, val text, expire timestamptz) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_prepend_absexpire' LANGUAGE c STRICT; CREATE FUNCTION memcache_prepend(key text, val text, expire interval) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_prepend' LANGUAGE c STRICT; CREATE FUNCTION memcache_prepend(key text, val text) RETURNS bool AS 'MODULE_PATHNAME', 'memcache_prepend' LANGUAGE c STRICT; CREATE FUNCTION memcache_incr(key text, increment bigint) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_incr' LANGUAGE c STRICT; CREATE FUNCTION memcache_incr(key text) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_incr' LANGUAGE c STRICT; CREATE FUNCTION memcache_decr(key text, decrement bigint) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_decr' LANGUAGE c STRICT; CREATE FUNCTION memcache_decr(key text) RETURNS bigint AS 'MODULE_PATHNAME', 'memcache_decr' LANGUAGE c STRICT; pgmemcache-2.3.0/localtests.sh000077500000000000000000000022351247037475500163410ustar00rootroot00000000000000#!/bin/bash -xue # Create a clean PostgreSQL cluster for our testing TESTDIR="$(pwd)/regressiondata" export PGPORT=$((10240 + RANDOM / 2)) export PGDATA="$TESTDIR/pg" rm -rf "$TESTDIR" mkdir -p "$PGDATA" initdb -E UTF-8 --no-locale sed -e "s%^#port =.*%port = $PGPORT%" \ -e "s%^#\(unix_socket_director[a-z]*\) =.*%\1 = '$PGDATA'%" \ -e "s%^#dynamic_library_path = .*%dynamic_library_path = '$(pwd):\$libdir'%" \ -e "s%^#fsync = .*%fsync = off%" \ -e "s%^#synchronous_commit = .*%synchronous_commit = off%" \ -i "$PGDATA/postgresql.conf" pg_ctl -l "$PGDATA/log" start while [ ! -S "$PGDATA/.s.PGSQL.$PGPORT" ]; do sleep 2; done trap "pg_ctl stop -m immediate" EXIT # It's not possible to override the extension path, so we'll just execute # the extension SQL directly after mangling it a bit with sed cp -a Makefile test.sql sql/ expected/ "$TESTDIR" sed -e "s%MODULE_PATHNAME%pgmemcache%" \ -e "/CREATE EXTENSION/d" -e "/^--/d" -e "/^$/d" \ "ext/pgmemcache.sql" > "$TESTDIR/sql/init.sql" cp "$TESTDIR/sql/init.sql" "$TESTDIR/expected/init.out" # Run the actual tests make -C "$TESTDIR" installcheck REGRESS_OPTS="--host=$PGDATA --port=$PGPORT" pgmemcache-2.3.0/pgmemcache.c000066400000000000000000001051621247037475500160650ustar00rootroot00000000000000/* * PostgreSQL functions to interface with memcache. * * Copyright (c) 2004-2005 Sean Chittenden * Copyright (c) 2007-2008 Neil Conway * Copyright (c) 2007 Open Technology Group, Inc. * Copyright (c) 2008-2013 Hannu Valtonen * Copyright (c) 2012-2014 Ohmu Ltd * * See the file LICENSE for distribution terms. */ #ifdef USE_OMCACHE #define OMCACHE_READ_TIMEOUT 2000 #include "omcache_libmemcached.h" #include /* for log levels */ #endif /* USE_OMCACHE */ #ifdef USE_LIBMEMCACHED #include #endif /* USE_LIBMEMCACHED */ #include "pgmemcache.h" #define KEY_MAX_LENGTH 250 #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif /* Internal functions */ static void pgmemcache_reset_context(void); static void pgmemcache_xact_callback(XactEvent event, void *arg); static void assign_sasl_params(const char *username, const char *password); static void assign_default_servers_guc(const char *newval, void *extra); static void assign_default_behavior_guc(const char *newval, void *extra); static memcached_behavior get_memcached_behavior_flag(const char *flag); static uint64_t get_memcached_behavior_data(const char *flag, const char *data, const char **val); static Datum memcache_set_cmd(int type, PG_FUNCTION_ARGS); static memcached_return do_server_add(const char *host_str); const char *get_arg_cstring(text *text_field, size_t *length, bool is_key); /* Per-backend global state. */ static struct memcache_global_s { memcached_st *mc; bool flush_needed; bool flush_on_commit; char *default_servers; char *default_behavior; char *sasl_authentication_username; char *sasl_authentication_password; } globals; void _PG_init(void) { pgmemcache_reset_context(); DefineCustomStringVariable("pgmemcache.default_servers", "Comma-separated list of memcached servers to connect to.", "Specified as a comma-separated list of host:port (port is optional).", &globals.default_servers, NULL, PGC_USERSET, GUC_LIST_INPUT, #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90100) NULL, #endif assign_default_servers_guc, NULL); DefineCustomStringVariable("pgmemcache.default_behavior", "Comma-separated list of memcached behavior (optional).", "Specified as a comma-separated list of behavior_flag:behavior_data.", &globals.default_behavior, NULL, PGC_USERSET, GUC_LIST_INPUT, #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90100) NULL, #endif assign_default_behavior_guc, NULL); DefineCustomBoolVariable("pgmemcache.flush_on_commit", "Whether to flush all buffers to memcached on end of commit", NULL, &globals.flush_on_commit, false, PGC_USERSET, 0, #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90100) NULL, #endif NULL, NULL); DefineCustomStringVariable("pgmemcache.sasl_authentication_username", "pgmemcache SASL user authentication username", "Simple string pgmemcache.sasl_authentication_username = 'testing_username'", &globals.sasl_authentication_username, NULL, PGC_USERSET, GUC_LIST_INPUT, #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90100) NULL, #endif NULL, NULL); DefineCustomStringVariable("pgmemcache.sasl_authentication_password", "pgmemcache SASL user authentication password", "Simple string pgmemcache.sasl_authentication_password = 'testing_password'", &globals.sasl_authentication_password, NULL, PGC_USERSET, GUC_LIST_INPUT, #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90100) NULL, #endif NULL, NULL); /* XXX: We should have real assign_hook for these so that memcache sasl data * is updated when the values are changed. */ assign_sasl_params(globals.sasl_authentication_username, globals.sasl_authentication_password); RegisterXactCallback(pgmemcache_xact_callback, NULL); } /* This is called when we're being unloaded from a process. Note that * this only happens when we're being replaced by a LOAD (e.g. it * doesn't happen on process exit), so we can't depend on it being * called. */ void _PG_fini(void) { memcached_free(globals.mc); } static time_t interval_to_time_t(Interval *span) { float8 result; #ifdef HAVE_INT64_TIMESTAMP result = span->time / 1000000e0; #else result = span->time; #endif result += span->day * 86400; if (span->month != 0) { result += (365.25 * 86400) * (span->month / 12); result += (30.0 * 86400) * (span->month % 12); } return (time_t) result; } /* called at end of transaction, flush all buffers to memcache */ static void pgmemcache_xact_callback(XactEvent event, void *arg) { if (globals.flush_on_commit && globals.flush_needed && (event == XACT_EVENT_COMMIT #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90300) || event == XACT_EVENT_PRE_COMMIT #endif /* PG_VERSION_NUM >= 90300 */ )) { #ifdef USE_LIBMEMCACHED memcached_return rc = memcached_flush_buffers(globals.mc); #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE int rc = omcache_io(globals.mc, NULL, NULL, NULL, NULL, OMCACHE_READ_TIMEOUT); #endif /* USE_OMCACHE */ if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: memcached_flush_buffers: %s", memcached_strerror(globals.mc, rc)); else globals.flush_needed = false; } } #ifdef USE_OMCACHE static void pgmemcache_log_func(void *context, int syslog_level, const char *msg) { int pg_level = LOG_INFO; switch (syslog_level) { case LOG_ERR: case LOG_WARNING: pg_level = WARNING; break; case LOG_NOTICE: pg_level = NOTICE; break; } elog(pg_level, "%s", msg); } #endif /* USE_OMCACHE */ static void pgmemcache_reset_context(void) { if (globals.mc) { memcached_free(globals.mc); globals.mc = NULL; } globals.mc = memcached_create(NULL); #ifdef USE_OMCACHE omcache_set_log_callback(globals.mc, 0, pgmemcache_log_func, NULL); #endif /* USE_OMCACHE */ #ifdef USE_LIBMEMCACHED /* Always use the memcache binary protocol as required for memcached_(increment|decrement)_with_initial. */ { int rc = memcached_behavior_set(globals.mc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: memcached_behavior_set(BINARY_PROTOCOL, 1): %s", memcached_strerror(globals.mc, rc)); } #endif /* USE_LIBMEMCACHED */ assign_default_behavior_guc(globals.default_behavior, NULL); assign_sasl_params(globals.sasl_authentication_username, globals.sasl_authentication_password); } static void assign_sasl_params(const char *username, const char *password) { #if LIBMEMCACHED_WITH_SASL_SUPPORT if (username != NULL && strlen(username) > 0 && password != NULL && strlen(password) > 0) { int rc = memcached_set_sasl_auth_data(globals.mc, username, password); if (rc != MEMCACHED_SUCCESS) elog(ERROR, "pgmemcache: memcached_set_sasl_auth_data: %s", memcached_strerror(globals.mc, rc)); rc = sasl_client_init(NULL); if (rc != SASL_OK) elog(ERROR, "pgmemcache: sasl_client_init failed: %d", rc); } #endif } static void assign_default_servers_guc(const char *newval, void *extra) { if (newval) { #ifdef USE_LIBMEMCACHED /* there is no way to remove servers from a memcache context, so * recreate it from scratch when the server list changes */ pgmemcache_reset_context(); #endif /* USE_LIBMEMCACHED */ do_server_add(newval); } } static void assign_default_behavior_guc(const char *newval, void *extra) { int i, len; StringInfoData flag_buf; StringInfoData data_buf; memcached_return rc; memcached_behavior bkey; uint64_t bval; const char *bvalstr = ""; if (!newval) return; initStringInfo(&flag_buf); initStringInfo(&data_buf); len = strlen(newval); for (i = 0; i < len; i++) { char c = newval[i]; if (c == ',' || c == ':') { if (flag_buf.len == 0) return; if (c == ':') { int j; for (j = i + 1; j < len; j++) { if (newval[j] == ',') break; appendStringInfoChar(&data_buf, newval[j]); } if (data_buf.len == 0) return; i += data_buf.len; } rc = MEMCACHED_FAILURE; bkey = get_memcached_behavior_flag(flag_buf.data); bval = get_memcached_behavior_data(flag_buf.data, data_buf.data, &bvalstr); #ifdef USE_OMCACHE if (bkey == NULL) rc = OMCACHE_FAIL; else if (strcmp(bkey, "BINARY_PROTOCOL") == 0) { if (!bval) elog(ERROR, "pgmemcache: omcache always uses binary protocol"); rc = OMCACHE_OK; } else if (strcmp(bkey, "BUFFER_REQUESTS") == 0) rc = omcache_set_buffering(globals.mc, bval); else if (strcmp(bkey, "CONNECT_TIMEOUT") == 0) rc = omcache_set_connect_timeout(globals.mc, bval); else if (strcmp(bkey, "DEAD_TIMEOUT") == 0) rc = omcache_set_dead_timeout(globals.mc, bval * 1000); else if (strcmp(bkey, "DISTRIBUTION") == 0) { if (strcmp(bvalstr, "CONSISTENT") && strcmp(bvalstr, "CONSISTENT_KETAMA")) elog(ERROR, "pgmemcache: omcache always uses ketama"); rc = OMCACHE_OK; } else if (strcmp(bkey, "HASH") == 0 || strcmp(bkey, "KETAMA_HASH") == 0) { if (strcmp(bvalstr, "DEFAULT")) elog(ERROR, "pgmemcache: omcache always uses the 'default' (bob jenkins' 'one at a time') hash"); rc = OMCACHE_OK; } else if (strcmp(bkey, "KETAMA") == 0) { if (!bval) elog(ERROR, "pgmemcache: omcache always uses a ketama distribution method"); rc = omcache_set_distribution_method(globals.mc, &omcache_dist_libmemcached_ketama); } else if (strcmp(bkey, "KETAMA_WEIGHTED") == 0) { if (!bval) elog(ERROR, "pgmemcache: omcache always uses a ketama distribution method"); rc = omcache_set_distribution_method(globals.mc, &omcache_dist_libmemcached_ketama_weighted); } else if (strcmp(bkey, "KETAMA_PRE1010") == 0) { if (!bval) elog(ERROR, "pgmemcache: omcache always uses a ketama distribution method"); rc = omcache_set_distribution_method(globals.mc, &omcache_dist_libmemcached_ketama_pre1010); } else if (strcmp(bkey, "NO_BLOCK") == 0) rc = OMCACHE_OK; // omcache is non-blocking by default else if (strcmp(bkey, "NOREPLY") == 0) rc = OMCACHE_OK; // this isn't really a behavior in omcache else if (strcmp(bkey, "REMOVE_FAILED_SERVERS") == 0) rc = OMCACHE_OK; // omcache doesn't have this concept else if (strcmp(bkey, "RETRY_TIMEOUT") == 0) rc = omcache_set_reconnect_timeout(globals.mc, bval * 1000); else if (strcmp(bkey, "SUPPORT_CAS") == 0) rc = OMCACHE_OK; // omcache uses binary protocol which always has cas else { elog(ERROR, "pgmemcache: unsupported behavior %s for omcache", flag_buf.data); } #endif /* USE_OMCACHE */ #ifdef USE_LIBMEMCACHED rc = memcached_behavior_set(globals.mc, bkey, bval); #endif /* USE_LIBMEMCACHED */ if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: memcached_behavior_set: %s", memcached_strerror(globals.mc, rc)); /* Skip the element separator, reset buffers */ i++; flag_buf.data[0] = '\0'; flag_buf.len = 0; data_buf.data[0] = '\0'; data_buf.len = 0; } else { appendStringInfoChar(&flag_buf, c); } } pfree(flag_buf.data); pfree(data_buf.data); } Datum memcache_add(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_ADD | PG_MEMCACHE_TYPE_INTERVAL, fcinfo); } Datum memcache_add_absexpire(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_ADD | PG_MEMCACHE_TYPE_TIMESTAMP, fcinfo); } const char *get_arg_cstring(text *text_field, size_t *length, bool is_key) { *length = VARSIZE(text_field) - VARHDRSZ; if (is_key && *length < 1) elog(ERROR, "pgmemcache: key cannot be an empty string"); else if (is_key && *length > KEY_MAX_LENGTH) elog(ERROR, "pgmemcache: key too long, maximum is %d characters", KEY_MAX_LENGTH); return VARDATA(text_field); } static Datum memcache_delta_op(bool increment, PG_FUNCTION_ARGS) { uint64_t val; int64_t offset = 1; memcached_return rc; size_t key_length; const char *key = get_arg_cstring(PG_GETARG_TEXT_P(0), &key_length, true); if (PG_NARGS() >= 2) offset = PG_GETARG_INT64(1); if (offset < 0) { /* memcached uses uint64_t but postgresql only has signed types, but * since we have both increment and decrement operations let's just * invert the operation if offset is negative. */ offset = abs(offset); increment = !increment; } if (increment) rc = memcached_increment_with_initial(globals.mc, key, key_length, offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, &val); else rc = memcached_decrement_with_initial(globals.mc, key, key_length, offset, 0, MEMCACHED_EXPIRATION_NOT_ADD, &val); if (rc == MEMCACHED_BUFFERED) { globals.flush_needed = true; PG_RETURN_NULL(); } if (rc != MEMCACHED_SUCCESS) { elog(WARNING, "pgmemcache: memcached_%s_with_initial: %s", increment ? "increment" : "decrement", memcached_strerror(globals.mc, rc)); } else if (val > 0x7FFFFFFFFFFFFFFFLL && val != UINT64_MAX) { /* Cannot represent uint64_t values above 2^63-1 with BIGINT. Do not signal error for UINT64_MAX which just means there was no reply. */ elog(ERROR, "pgmemcache: memcached_%s_with_initial: %s", increment ? "increment" : "decrement", "value received from memcache is out of BIGINT range"); } PG_RETURN_INT64(val); } Datum memcache_decr(PG_FUNCTION_ARGS) { return memcache_delta_op(false, fcinfo); } Datum memcache_incr(PG_FUNCTION_ARGS) { return memcache_delta_op(true, fcinfo); } Datum memcache_delete(PG_FUNCTION_ARGS) { time_t hold; memcached_return rc; size_t key_length; const char *key = get_arg_cstring(PG_GETARG_TEXT_P(0), &key_length, true); hold = (time_t) 0.0; if (PG_NARGS() >= 2 && PG_ARGISNULL(1) == false) hold = interval_to_time_t(PG_GETARG_INTERVAL_P(1)); rc = memcached_delete(globals.mc, key, key_length, hold); if (rc == MEMCACHED_BUFFERED) { globals.flush_needed = true; PG_RETURN_NULL(); } if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTFOUND) elog(WARNING, "pgmemcache: memcached_delete: %s", memcached_strerror(globals.mc, rc)); PG_RETURN_BOOL(rc == MEMCACHED_SUCCESS); } Datum memcache_flush_all0(PG_FUNCTION_ARGS) { static time_t opt_expire = 0; memcached_return rc; rc = memcached_flush(globals.mc, opt_expire); if (rc == MEMCACHED_BUFFERED) { globals.flush_needed = true; PG_RETURN_NULL(); } if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: memcached_flush: %s", memcached_strerror(globals.mc, rc)); PG_RETURN_BOOL(rc == MEMCACHED_SUCCESS); } Datum memcache_get(PG_FUNCTION_ARGS) { text *ret; #ifdef USE_LIBMEMCACHED char *string; uint32_t flags; #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE const unsigned char *string; #endif /* USE_OMCACHE */ size_t return_value_length; memcached_return rc; size_t key_length; const char *key = get_arg_cstring(PG_GETARG_TEXT_P(0), &key_length, true); #ifdef USE_LIBMEMCACHED string = memcached_get(globals.mc, key, key_length, &return_value_length, &flags, &rc); #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE rc = omcache_get(globals.mc, omc_cc_to_cuc(key), key_length, &string, &return_value_length, NULL, NULL, OMCACHE_READ_TIMEOUT); #endif /* USE_OMCACHE */ if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_NOTFOUND) elog(ERROR, "pgmemcache: memcached_get: %s", memcached_strerror(globals.mc, rc)); if (rc == MEMCACHED_NOTFOUND) PG_RETURN_NULL(); ret = (text *) palloc(return_value_length + VARHDRSZ); SET_VARSIZE(ret, return_value_length + VARHDRSZ); memcpy(VARDATA(ret), string, return_value_length); #ifdef USE_LIBMEMCACHED free(string); #endif /* USE_LIBMEMCACHED */ PG_RETURN_TEXT_P(ret); } Datum memcache_get_multi(PG_FUNCTION_ARGS) { ArrayType *array; int array_length, array_lbound, i; Oid element_type; memcached_return rc; char typalign; int16 typlen; bool typbyval; #ifdef USE_LIBMEMCACHED uint32_t flags; char *current_key, *current_val; #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE const unsigned char *current_key, *current_val; #endif /* USE_OMCACHE */ size_t current_key_len, current_val_len; FuncCallContext *funcctx; MemoryContext oldcontext; TupleDesc tupdesc; AttInMetadata *attinmeta; struct internal_fctx { size_t *key_lens; #ifdef USE_LIBMEMCACHED const char **keys; #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE const unsigned char **keys; omcache_req_t *requests; size_t request_count; omcache_value_t *values; size_t value_count; #endif /* USE_OMCACHE */ } *fctx; array = PG_GETARG_ARRAYTYPE_P(0); if (ARR_NDIM(array) != 1) elog(ERROR, "pgmemcache: only single dimension ARRAYs are supported, " "not ARRAYs with %d dimensions", ARR_NDIM(array)); array_lbound = ARR_LBOUND(array)[0]; array_length = ARR_DIMS(array)[0]; element_type = ARR_ELEMTYPE(array); if (SRF_IS_FIRSTCALL()) { /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->max_calls = array_length; if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context that cannot accept type record"))); get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); fctx = (struct internal_fctx *) palloc(sizeof(*fctx)); /* extra NULL key for last memcached_fetch call */ fctx->keys = palloc(sizeof(char *) * (array_length + 1)); fctx->key_lens = palloc(sizeof(size_t) * (array_length + 1)); fctx->keys[array_length] = 0; fctx->key_lens[array_length] = 0; #ifdef USE_LIBMEMCACHED for (i = 0; i < array_length; i++) { int offset = array_lbound + i; bool isnull; Datum elem = array_ref(array, 1, &offset, 0, typlen, typbyval, typalign, &isnull); if (!isnull) fctx->keys[i] = get_arg_cstring(DatumGetTextP(elem), &fctx->key_lens[i], true); } rc = memcached_mget(globals.mc, fctx->keys, fctx->key_lens, array_length); if (rc != MEMCACHED_SUCCESS) elog(ERROR, "pgmemcache: memcached_mget: %s", memcached_strerror(globals.mc, rc)); #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE /* persistent request structures to handle pending requests */ fctx->requests = palloc(sizeof(omcache_req_t) * array_length); fctx->request_count = array_length; fctx->values = palloc(sizeof(omcache_value_t) * array_length); fctx->value_count = array_length; for (i = 0; i < array_length; i++) { int offset = array_lbound + i; bool isnull; Datum elem = array_ref(array, 1, &offset, 0, typlen, typbyval, typalign, &isnull); if (!isnull) fctx->keys[i] = (const unsigned char *) get_arg_cstring(DatumGetTextP(elem), &fctx->key_lens[i], true); } rc = omcache_get_multi(globals.mc, fctx->keys, fctx->key_lens, array_length, fctx->requests, &fctx->request_count, fctx->values, &fctx->value_count, OMCACHE_READ_TIMEOUT); if (rc != OMCACHE_OK && rc != OMCACHE_AGAIN) elog(ERROR, "pgmemcache: omcache_get_multi: %s", omcache_strerror(rc)); #endif /* USE_OMCACHE */ attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = funcctx->user_fctx; attinmeta = funcctx->attinmeta; #ifdef USE_LIBMEMCACHED current_key = (char *) fctx->keys[funcctx->call_cntr]; // cast away constness for libmemcached api current_key_len = fctx->key_lens[funcctx->call_cntr]; current_val = memcached_fetch(globals.mc, current_key, ¤t_key_len, ¤t_val_len, &flags, &rc); if (rc == MEMCACHED_END) { SRF_RETURN_DONE(funcctx); } else if (rc != MEMCACHED_SUCCESS) { elog(ERROR, "pgmemcache: memcached_fetch: %s", memcached_strerror(globals.mc, rc)); SRF_RETURN_DONE(funcctx); } #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE current_val = NULL; if (fctx->value_count == 0 && fctx->request_count > 0) { fctx->value_count = fctx->request_count; rc = omcache_io(globals.mc, fctx->requests, &fctx->request_count, fctx->values, &fctx->value_count, OMCACHE_READ_TIMEOUT); if (rc != OMCACHE_OK && rc != OMCACHE_AGAIN) elog(ERROR, "pgmemcache: omcache_io: %s", omcache_strerror(rc)); } if (fctx->value_count > 0) { fctx->value_count --; current_key = fctx->values[fctx->value_count].key; current_key_len = fctx->values[fctx->value_count].key_len; current_val = fctx->values[fctx->value_count].data; current_val_len = fctx->values[fctx->value_count].data_len; } #endif /* USE_OMCACHE */ if (current_val != NULL) { char **values; HeapTuple tuple; Datum result; values = (char **) palloc(2 * sizeof(char *)); /* make sure we have space for terminating zero character */ values[0] = (char *) palloc(current_key_len + 1); values[1] = (char *) palloc(current_val_len + 1); memcpy(values[0], current_key, current_key_len); memcpy(values[1], current_val, current_val_len); #ifdef USE_LIBMEMCACHED free(current_val); #endif /* USE_LIBMEMCACHED */ /* BuildTupleFromCStrings needs correct zero-terminated C-string, so terminate our raw strings */ values[0][current_key_len] = '\0'; values[1][current_val_len] = '\0'; tuple = BuildTupleFromCStrings(attinmeta, values); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } SRF_RETURN_DONE(funcctx); } Datum memcache_replace(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_REPLACE | PG_MEMCACHE_TYPE_INTERVAL, fcinfo); } Datum memcache_replace_absexpire(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_REPLACE | PG_MEMCACHE_TYPE_TIMESTAMP, fcinfo); } Datum memcache_set(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_SET | PG_MEMCACHE_TYPE_INTERVAL, fcinfo); } Datum memcache_set_absexpire(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_SET | PG_MEMCACHE_TYPE_TIMESTAMP, fcinfo); } Datum memcache_prepend(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_PREPEND | PG_MEMCACHE_TYPE_INTERVAL, fcinfo); } Datum memcache_prepend_absexpire(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_PREPEND | PG_MEMCACHE_TYPE_TIMESTAMP, fcinfo); } Datum memcache_append(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_APPEND | PG_MEMCACHE_TYPE_INTERVAL, fcinfo); } Datum memcache_append_absexpire(PG_FUNCTION_ARGS) { return memcache_set_cmd(PG_MEMCACHE_CMD_APPEND | PG_MEMCACHE_TYPE_TIMESTAMP, fcinfo); } static Datum memcache_set_cmd(int type, PG_FUNCTION_ARGS) { memcached_return rc = MEMCACHED_FAILURE; const char *func = NULL; time_t expiration = 0; size_t key_length, value_length; const char *key = get_arg_cstring(PG_GETARG_TEXT_P(0), &key_length, true); const char *value = get_arg_cstring(PG_GETARG_TEXT_P(1), &value_length, false); if (PG_NARGS() >= 3 && PG_ARGISNULL(2) == false) { if (type & PG_MEMCACHE_TYPE_INTERVAL) { Interval *span = PG_GETARG_INTERVAL_P(2); expiration = interval_to_time_t(span); } else if (type & PG_MEMCACHE_TYPE_TIMESTAMP) { TimestampTz timestamptz; struct pg_tm tm; fsec_t fsec; timestamptz = PG_GETARG_TIMESTAMPTZ(2); /* convert to timestamptz to produce consistent results */ if (timestamp2tm(timestamptz, NULL, &tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); #ifdef HAVE_INT64_TIMESTAMP expiration = (time_t) ((timestamptz - SetEpochTimestamp()) / 1000000e0); #else expiration = (time_t) timestamptz - SetEpochTimestamp(); #endif } else { elog(ERROR, "%s():%s:%u: invalid date type", __FUNCTION__, __FILE__, __LINE__); } } switch (type & PG_MEMCACHE_CMD_MASK) { case PG_MEMCACHE_CMD_ADD: func = "memcached_add"; rc = memcached_add(globals.mc, key, key_length, value, value_length, expiration, 0); break; case PG_MEMCACHE_CMD_REPLACE: func = "memcached_replace"; rc = memcached_replace(globals.mc, key, key_length, value, value_length, expiration, 0); break; case PG_MEMCACHE_CMD_SET: func = "memcached_set"; rc = memcached_set(globals.mc, key, key_length, value, value_length, expiration, 0); break; case PG_MEMCACHE_CMD_PREPEND: func = "memcached_prepend"; rc = memcached_prepend(globals.mc, key, key_length, value, value_length, expiration, 0); break; case PG_MEMCACHE_CMD_APPEND: func = "memcached_append"; rc = memcached_append(globals.mc, key, key_length, value, value_length, expiration, 0); break; default: elog(ERROR, "pgmemcache: unknown set command type: %d", type); } if (rc == MEMCACHED_BUFFERED) { globals.flush_needed = true; PG_RETURN_NULL(); } if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: %s: %s", func, memcached_strerror(globals.mc, rc)); PG_RETURN_BOOL(rc == MEMCACHED_SUCCESS); } Datum memcache_server_add(PG_FUNCTION_ARGS) { size_t host_len; const char *host_buf = get_arg_cstring(PG_GETARG_TEXT_P(0), &host_len, false); char *host = pnstrdup(host_buf, host_len); memcached_return rc = do_server_add(host); if (rc != MEMCACHED_SUCCESS) elog(WARNING, "pgmemcache: memcached_server_push: %s", memcached_strerror(globals.mc, rc)); PG_RETURN_BOOL(rc == MEMCACHED_SUCCESS); } static memcached_return do_server_add(const char *host_str) { memcached_server_st *servers; memcached_return rc; servers = memcached_servers_parse(host_str); rc = memcached_server_push(globals.mc, servers); memcached_server_list_free(servers); return rc; } #ifdef USE_LIBMEMCACHED #define MC_ENUM_INVAL -1 #define MC_STR_TO_ENUM(d,v) \ if (strcmp(value, "MEMCACHED_" #d "_" #v) == 0 || strcmp(value, #v) == 0) \ return MEMCACHED_##d##_##v #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE #define MC_ENUM_INVAL NULL #define MC_STR_TO_ENUM(d,v) \ if (strcmp(value, "MEMCACHED_" #d "_" #v) == 0 || strcmp(value, #v) == 0) \ return #v #endif /* USE_OMCACHE */ static memcached_behavior get_memcached_behavior_flag(const char *value) { MC_STR_TO_ENUM(BEHAVIOR, BINARY_PROTOCOL); MC_STR_TO_ENUM(BEHAVIOR, BUFFER_REQUESTS); MC_STR_TO_ENUM(BEHAVIOR, CACHE_LOOKUPS); MC_STR_TO_ENUM(BEHAVIOR, CONNECT_TIMEOUT); #if LIBMEMCACHED_VERSION_HEX >= 0x01000003 MC_STR_TO_ENUM(BEHAVIOR, DEAD_TIMEOUT); #endif MC_STR_TO_ENUM(BEHAVIOR, DISTRIBUTION); MC_STR_TO_ENUM(BEHAVIOR, HASH); MC_STR_TO_ENUM(BEHAVIOR, HASH_WITH_PREFIX_KEY); MC_STR_TO_ENUM(BEHAVIOR, IO_BYTES_WATERMARK); MC_STR_TO_ENUM(BEHAVIOR, IO_KEY_PREFETCH); MC_STR_TO_ENUM(BEHAVIOR, IO_MSG_WATERMARK); MC_STR_TO_ENUM(BEHAVIOR, KETAMA); MC_STR_TO_ENUM(BEHAVIOR, KETAMA_HASH); #ifdef USE_OMCACHE MC_STR_TO_ENUM(BEHAVIOR, KETAMA_PRE1010); #endif MC_STR_TO_ENUM(BEHAVIOR, KETAMA_WEIGHTED); MC_STR_TO_ENUM(BEHAVIOR, NO_BLOCK); MC_STR_TO_ENUM(BEHAVIOR, NOREPLY); MC_STR_TO_ENUM(BEHAVIOR, NUMBER_OF_REPLICAS); MC_STR_TO_ENUM(BEHAVIOR, POLL_TIMEOUT); MC_STR_TO_ENUM(BEHAVIOR, RANDOMIZE_REPLICA_READ); MC_STR_TO_ENUM(BEHAVIOR, RCV_TIMEOUT); #if LIBMEMCACHED_VERSION_HEX >= 0x00049000 MC_STR_TO_ENUM(BEHAVIOR, REMOVE_FAILED_SERVERS); #endif MC_STR_TO_ENUM(BEHAVIOR, RETRY_TIMEOUT); MC_STR_TO_ENUM(BEHAVIOR, SERVER_FAILURE_LIMIT); MC_STR_TO_ENUM(BEHAVIOR, SND_TIMEOUT); MC_STR_TO_ENUM(BEHAVIOR, SOCKET_RECV_SIZE); MC_STR_TO_ENUM(BEHAVIOR, SOCKET_SEND_SIZE); MC_STR_TO_ENUM(BEHAVIOR, SORT_HOSTS); MC_STR_TO_ENUM(BEHAVIOR, SUPPORT_CAS); MC_STR_TO_ENUM(BEHAVIOR, TCP_NODELAY); MC_STR_TO_ENUM(BEHAVIOR, USER_DATA); MC_STR_TO_ENUM(BEHAVIOR, USE_UDP); MC_STR_TO_ENUM(BEHAVIOR, VERIFY_KEY); elog(ERROR, "pgmemcache: unknown behavior flag: %s", value); return MC_ENUM_INVAL; } static memcached_hash get_memcached_hash_type(const char *value) { MC_STR_TO_ENUM(HASH, MURMUR); MC_STR_TO_ENUM(HASH, MD5); MC_STR_TO_ENUM(HASH, JENKINS); MC_STR_TO_ENUM(HASH, HSIEH); MC_STR_TO_ENUM(HASH, FNV1A_64); MC_STR_TO_ENUM(HASH, FNV1A_32); MC_STR_TO_ENUM(HASH, FNV1_64); MC_STR_TO_ENUM(HASH, FNV1_32); MC_STR_TO_ENUM(HASH, DEFAULT); MC_STR_TO_ENUM(HASH, CRC); elog(ERROR, "pgmemcache: invalid hash name: %s", value); return MC_ENUM_INVAL; } static memcached_server_distribution get_memcached_distribution_type(const char *value) { MC_STR_TO_ENUM(DISTRIBUTION, RANDOM); MC_STR_TO_ENUM(DISTRIBUTION, MODULA); MC_STR_TO_ENUM(DISTRIBUTION, CONSISTENT_KETAMA); MC_STR_TO_ENUM(DISTRIBUTION, CONSISTENT); elog(ERROR, "pgmemcache: invalid distribution name: %s", value); return MC_ENUM_INVAL; } static uint64_t get_memcached_behavior_data(const char *flag, const char *data, const char **val) { char *endptr; uint64_t ret; memcached_behavior bkey = get_memcached_behavior_flag(flag); #ifdef USE_LIBMEMCACHED switch (bkey) { case MEMCACHED_BEHAVIOR_HASH: case MEMCACHED_BEHAVIOR_KETAMA_HASH: return get_memcached_hash_type(data); case MEMCACHED_BEHAVIOR_DISTRIBUTION: return get_memcached_distribution_type(data); default: #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE ret = 0; if (strcmp(bkey, "HASH") == 0 || strcmp(bkey, "KETAMA_HASH") == 0) *val = get_memcached_hash_type(data); else if (strcmp(bkey, "DISTRIBUTION") == 0) *val = get_memcached_distribution_type(data); else { #endif /* USE_OMCACHE */ ret = strtol(data, &endptr, 10); if (endptr == data) elog(ERROR, "pgmemcache: invalid behavior param %s: %s", flag, data); } return ret; } /* NOTE: memcached_server_fn specifies that the first argument is const, but * memcached_stat_get_keys wants a non-const argument so we don't define it * as const here. */ static memcached_return_t server_stat_function(memcached_st *mc, memcached_server_instance_st server, void *context) { memcached_return rc; StringInfoData *strbuf = (StringInfoData *) context; const char *hostname = memcached_server_name(server); unsigned int port = memcached_server_port(server); appendStringInfo(strbuf, "Server: %s (%u)\n", hostname, port); { #ifdef USE_LIBMEMCACHED memcached_stat_st stat; char **list, **stat_ptr; rc = memcached_stat_servername(&stat, NULL, hostname, port); if (rc != MEMCACHED_SUCCESS) return rc; list = memcached_stat_get_keys(mc, &stat, &rc); if (rc != MEMCACHED_SUCCESS) return rc; for (stat_ptr = list; stat_ptr && *stat_ptr; stat_ptr++) { char *value = memcached_stat_get_value(mc, &stat, *stat_ptr, &rc); appendStringInfo(strbuf, "%s: %s\n", *stat_ptr, value); free(value); } free(list); #endif /* USE_LIBMEMCACHED */ #ifdef USE_OMCACHE size_t i, value_count = 50; omcache_value_t values[50]; rc = omcache_stat(globals.mc, NULL, values, &value_count, server->server_index, OMCACHE_READ_TIMEOUT); if (rc != OMCACHE_OK) { value_count = 0; appendStringInfo(strbuf, "omcache_stat failed: %s\n", omcache_strerror(rc)); } for (i = 0; i < value_count; i++) { int key_len = (int) values[i].key_len, data_len = (int) values[i].data_len; if (key_len == 0 && data_len == 0) break; appendStringInfo(strbuf, "%.*s: %.*s\n", key_len, (const char *) values[i].key, data_len, (const char *) values[i].data); } #endif /* USE_OMCACHE */ } appendStringInfo(strbuf, "\n"); return MEMCACHED_SUCCESS; } Datum memcache_stats(PG_FUNCTION_ARGS) { StringInfoData strbuf; memcached_return rc; memcached_server_fn callbacks[1]; initStringInfo(&strbuf); callbacks[0] = (memcached_server_fn) server_stat_function; rc = memcached_server_cursor(globals.mc, callbacks, (void *) &strbuf, 1); if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS) elog(WARNING, "pgmemcache: memcache_stats: %s", memcached_strerror(globals.mc, rc)); PG_RETURN_DATUM(DirectFunctionCall1(textin, CStringGetDatum(strbuf.data))); } pgmemcache-2.3.0/pgmemcache.h000066400000000000000000000057371247037475500161010ustar00rootroot00000000000000/* * PostgreSQL functions to interface with memcache. * * Copyright (c) 2004-2005 Sean Chittenden * Copyright (c) 2007-2008 Neil Conway * Copyright (c) 2007 Open Technology Group, Inc. * Copyright (c) 2008-2013 Hannu Valtonen * Copyright (c) 2012-2014 Ohmu Ltd * * See the file LICENSE for distribution terms. */ #ifndef PGMEMCACHE_H #define PGMEMCACHE_H #include "postgres.h" #include #include "access/heapam.h" #include "access/htup.h" #include "access/xact.h" #include "fmgr.h" #include "funcapi.h" #include "lib/stringinfo.h" #include "utils/builtins.h" #include "utils/datetime.h" #include "utils/guc.h" #include "utils/memutils.h" #include "utils/lsyscache.h" #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION void _PG_init(void); void _PG_fini(void); #define PG_MEMCACHE_CMD_ADD 0x0001 #define PG_MEMCACHE_CMD_REPLACE 0x0002 #define PG_MEMCACHE_CMD_SET 0x0004 #define PG_MEMCACHE_CMD_PREPEND 0x0008 #define PG_MEMCACHE_CMD_APPEND 0x0010 #define PG_MEMCACHE_CMD_MASK 0x00ff #define PG_MEMCACHE_TYPE_INTERVAL 0x0100 #define PG_MEMCACHE_TYPE_TIMESTAMP 0x0200 #define PG_MEMCACHE_TYPE_MASK 0x0f00 Datum memcache_add(PG_FUNCTION_ARGS); Datum memcache_add_absexpire(PG_FUNCTION_ARGS); Datum memcache_decr(PG_FUNCTION_ARGS); Datum memcache_delete(PG_FUNCTION_ARGS); Datum memcache_flush_all0(PG_FUNCTION_ARGS); Datum memcache_get(PG_FUNCTION_ARGS); Datum memcache_get_multi(PG_FUNCTION_ARGS); Datum memcache_incr(PG_FUNCTION_ARGS); Datum memcache_replace(PG_FUNCTION_ARGS); Datum memcache_replace_absexpire(PG_FUNCTION_ARGS); Datum memcache_server_add(PG_FUNCTION_ARGS); Datum memcache_set(PG_FUNCTION_ARGS); Datum memcache_set_absexpire(PG_FUNCTION_ARGS); Datum memcache_prepend(PG_FUNCTION_ARGS); Datum memcache_prepend_absexpire(PG_FUNCTION_ARGS); Datum memcache_append(PG_FUNCTION_ARGS); Datum memcache_append_absexpire(PG_FUNCTION_ARGS); Datum memcache_stats(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(memcache_add); PG_FUNCTION_INFO_V1(memcache_add_absexpire); PG_FUNCTION_INFO_V1(memcache_decr); PG_FUNCTION_INFO_V1(memcache_delete); PG_FUNCTION_INFO_V1(memcache_flush_all0); PG_FUNCTION_INFO_V1(memcache_get); PG_FUNCTION_INFO_V1(memcache_get_multi); PG_FUNCTION_INFO_V1(memcache_incr); PG_FUNCTION_INFO_V1(memcache_replace); PG_FUNCTION_INFO_V1(memcache_replace_absexpire); PG_FUNCTION_INFO_V1(memcache_server_add); PG_FUNCTION_INFO_V1(memcache_set); PG_FUNCTION_INFO_V1(memcache_set_absexpire); PG_FUNCTION_INFO_V1(memcache_prepend); PG_FUNCTION_INFO_V1(memcache_prepend_absexpire); PG_FUNCTION_INFO_V1(memcache_append); PG_FUNCTION_INFO_V1(memcache_append_absexpire); PG_FUNCTION_INFO_V1(memcache_stats); #endif /* !PGMEMCACHE_H */ pgmemcache-2.3.0/pgmemcache.spec000066400000000000000000000014261247037475500165730ustar00rootroot00000000000000Name: pgmemcache Version: %{major_version} Release: %{minor_version}%{?dist} Summary: PostgreSQL memcache functions Group: Applications/Databases License: MIT Source0: pgmemcache-rpm-src.tar.gz %description pgmemcache is a set of PostgreSQL user-defined functions that provide an interface to memcached. Installing pgmemcache is easy, but does have a few trivial requirements. %prep %setup -q -n %{name} %build make %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} %clean rm -rf %{buildroot} %files %defattr(-,root,root,-) %doc README.rst NEWS LICENSE %{_libdir}/pgsql/pgmemcache.so %{_datadir}/pgsql/extension/pgmemcache* %changelog * Mon Aug 19 2013 Oskari Saarenmaa - 2.1.1-11.g4e63c8a - Initial. pgmemcache-2.3.0/pgmemcache.te000066400000000000000000000005731247037475500162530ustar00rootroot00000000000000module pgmemcache 1.0; require { type postgresql_port_t; type postgresql_t; type http_cache_port_t; class tcp_socket name_connect; class unix_stream_socket connectto; } #============= postgresql_t ============== allow postgresql_t http_cache_port_t:tcp_socket name_connect; allow postgresql_t http_cache_port_t:unix_stream_socket connectto; pgmemcache-2.3.0/sql/000077500000000000000000000000001247037475500144225ustar00rootroot00000000000000pgmemcache-2.3.0/sql/init.sql000066400000000000000000000000351247037475500161040ustar00rootroot00000000000000CREATE EXTENSION pgmemcache; pgmemcache-2.3.0/sql/start_memcached.sql000066400000000000000000000000741247037475500202670ustar00rootroot00000000000000\! memcached -p 33211 -P $HOME/.pgmemcache-memcached.pid -d pgmemcache-2.3.0/sql/stop_memcached.sql000066400000000000000000000000561247037475500201170ustar00rootroot00000000000000\! kill `cat $HOME/.pgmemcache-memcached.pid` pgmemcache-2.3.0/sql/test.sql000066400000000000000000000000141247037475500161150ustar00rootroot00000000000000\i test.sql pgmemcache-2.3.0/test.sql000066400000000000000000000006561247037475500153320ustar00rootroot00000000000000SELECT memcache_server_add('localhost:33211'); SELECT regexp_replace(memcache_stats(), 'pid:.*', '') AS memcache_stats; SELECT memcache_delete('jeah'); SELECT memcache_set('jeah','test_value1'); SELECT memcache_add('counter','10'); SELECT memcache_incr('counter', 30); SELECT memcache_get('counter'); SELECT memcache_decr('counter'); SELECT memcache_delete('counter'); SELECT memcache_get('counter'); SELECT memcache_get('jeah');