From efe97b7b74874087caf1ade3514884ae72b3ba12 Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Wed, 22 Apr 2015 18:36:59 +0300
Subject: [PATCH] Define public module API for Tarantool Lua/C modules

This patch adds `src/module.h` header file with definition of all public
API functions suitable to use in Lua/C modules for Tarantool. This header
is automatically generated from src/*.h by CMake (`make generate_module_api`)
by extracting code blocks between /** \cond public */ /* \endcond public */
Doxygen-style comments.

Definitions of functions in module.h are frozen and must not be changed by
future commits/releases. A new test case `app/module_api` is added to check
compilation, linkage and loading of Lua modules and functionality of
provided API functions.

Other changes:

 * Convert mysql and pg to use module.h and prepare to convert to .c
 * Fix includes in say.h, coeio.h
 * Rename luaL_pushnumber64 to luaL_pushuint64 and
          luaL_pushinumber64 to luaL_pushint64
 * Add LUA_API macros to public luaL_* functions (actually adds extern "C")
---
 .gitignore                        |   1 +
 cmake/utils.cmake                 |  20 ++++++
 extra/apigen                      |   3 +
 extra/rpm/tarantool.rpm.spec.in   |   1 +
 src/CMakeLists.txt                |   5 ++
 src/box/lua/info.cc               |   5 +-
 src/box/lua/slab.cc               |  20 +++---
 src/box/vclock.c                  |   2 +-
 src/coeio.cc                      |  16 +++--
 src/coeio.h                       |  22 +++----
 src/evio.cc                       |   2 +
 src/fio.c                         |   1 +
 src/fiob.c                        |   1 +
 src/lib/small/region.h            |   1 +
 src/lua/init.cc                   |   2 +-
 src/lua/msgpack.cc                |   4 +-
 src/lua/pickle.cc                 |   4 +-
 src/lua/utils.cc                  |   4 +-
 src/lua/utils.h                   |  22 +++++--
 src/module/mysql/CMakeLists.txt   |   1 +
 src/module/mysql/mysql.cc         |  23 +++----
 src/module/pg/CMakeLists.txt      |   1 +
 src/module/pg/pg.cc               |  26 ++++----
 src/module_footer.h               |   5 ++
 src/module_header.h               |  20 ++++++
 src/say.h                         |  13 ++--
 src/sio.cc                        |   1 +
 test/CMakeLists.txt               |   2 +-
 test/app/CMakeLists.txt           |   4 ++
 test/app/module_api.c             | 103 ++++++++++++++++++++++++++++++
 test/app/module_api.result        |  11 ++++
 test/app/module_api.test.lua      |  20 ++++++
 third_party/lua-cjson/lua_cjson.c |   4 +-
 third_party/lua-yaml/lyaml.cc     |   4 +-
 34 files changed, 291 insertions(+), 83 deletions(-)
 create mode 100755 extra/apigen
 create mode 100644 src/module_footer.h
 create mode 100644 src/module_header.h
 create mode 100644 test/app/CMakeLists.txt
 create mode 100644 test/app/module_api.c
 create mode 100644 test/app/module_api.result
 create mode 100755 test/app/module_api.test.lua

diff --git a/.gitignore b/.gitignore
index 290c2d0411..85843c056c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@ src/box/bootstrap.h
 src/lua/*.lua.c
 src/box/lua/*.lua.c
 src/tarantool
+src/module.h
 tarantool-*.tar.gz
 test/lib/*.pyc
 test/lib/*/*.pyc
diff --git a/cmake/utils.cmake b/cmake/utils.cmake
index 95aad2eda8..197d83b1e9 100644
--- a/cmake/utils.cmake
+++ b/cmake/utils.cmake
@@ -79,3 +79,23 @@ function(bin_source varname srcfile dstfile)
         DEPENDS ${srcfile} bin2c)
 
 endfunction()
+
+# A helper function to extract public API
+function(apigen)
+    set (dstfile "${CMAKE_BINARY_DIR}/src/module.h")
+    set (tmpfile "${dstfile}.new")
+
+    add_custom_command(OUTPUT ${dstfile}
+        COMMAND cat ${CMAKE_SOURCE_DIR}/src/module_header.h > ${tmpfile}
+        COMMAND cat ${ARGN} | ${CMAKE_SOURCE_DIR}/extra/apigen >> ${tmpfile}
+        COMMAND cat ${CMAKE_SOURCE_DIR}/src/module_footer.h >> ${tmpfile}
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${tmpfile} ${dstfile}
+        COMMAND ${CMAKE_COMMAND} -E remove ${tmpfile}
+        DEPENDS ${srcfiles}
+                ${CMAKE_SOURCE_DIR}/src/module_header.h
+                ${CMAKE_SOURCE_DIR}/src/module_footer.h
+        )
+
+    add_custom_target(generate_module_api ALL DEPENDS ${dstfile})
+    install(FILES ${dstfile} DESTINATION ${MODULE_INCLUDEDIR})
+endfunction()
diff --git a/extra/apigen b/extra/apigen
new file mode 100755
index 0000000000..7352e94b31
--- /dev/null
+++ b/extra/apigen
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+sed -n '/^\/\*\* \\cond public \*\/$/,/^\/\*\* \\endcond public \*\/$/P'
diff --git a/extra/rpm/tarantool.rpm.spec.in b/extra/rpm/tarantool.rpm.spec.in
index 96cbdb18e3..19794386ae 100644
--- a/extra/rpm/tarantool.rpm.spec.in
+++ b/extra/rpm/tarantool.rpm.spec.in
@@ -300,6 +300,7 @@ chkconfig --del tarantool
 "%{_includedir}/tarantool/lua.hpp"
 "%{_includedir}/tarantool/luajit.h"
 "%{_includedir}/tarantool/lualib.h"
+"%{_includedir}/tarantool/module.h"
 
 %files common
 %defattr(-,root,root,-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 58d9173143..8b526bba7b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -143,8 +143,13 @@ endif()
 set (common_libraries ${common_libraries} ${LIBUUID_LIBRARIES})
 set (common_libraries ${common_libraries} PARENT_SCOPE)
 
+set(api_headers say.h coeio.h lua/utils.h)
+
 add_subdirectory(lib)
 add_subdirectory(box)
+
+apigen(${api_headers})
+
 # Save CMAKE_XXX_FLAGS from this directory for config.h (used in --version)
 set(TARANTOOL_C_FLAGS ${CMAKE_C_FLAGS} PARENT_SCOPE)
 set(TARANTOOL_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE)
diff --git a/src/box/lua/info.cc b/src/box/lua/info.cc
index 74f7a3b617..3c5eae1873 100644
--- a/src/box/lua/info.cc
+++ b/src/box/lua/info.cc
@@ -85,8 +85,7 @@ lbox_info_server(struct lua_State *L)
 	lua_pushlstring(L, tt_uuid_str(&recovery->server_uuid), UUID_STR_LEN);
 	lua_settable(L, -3);
 	lua_pushliteral(L, "lsn");
-	luaL_pushinumber64(L, vclock_get(&recovery->vclock,
-					 recovery->server_id));
+	luaL_pushint64(L, vclock_get(&recovery->vclock, recovery->server_id));
 	lua_settable(L, -3);
 	lua_pushliteral(L, "ro");
 	lua_pushboolean(L, box_is_ro());
@@ -103,7 +102,7 @@ lbox_info_vclock(struct lua_State *L)
 	luaL_setmaphint(L, -1);
 	vclock_foreach(&recovery->vclock, it) {
 		lua_pushinteger(L, it.id);
-		luaL_pushnumber64(L, it.lsn);
+		luaL_pushuint64(L, it.lsn);
 		lua_settable(L, -3);
 	}
 
diff --git a/src/box/lua/slab.cc b/src/box/lua/slab.cc
index e0c00a0e5c..131cd23443 100644
--- a/src/box/lua/slab.cc
+++ b/src/box/lua/slab.cc
@@ -66,27 +66,27 @@ small_stats_lua_cb(const struct mempool_stats *stats, void *cb_ctx)
 	luaL_setmaphint(L, -1);
 
 	lua_pushstring(L, "mem_used");
-	luaL_pushnumber64(L, stats->totals.used);
+	luaL_pushuint64(L, stats->totals.used);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "slab_size");
-	luaL_pushnumber64(L, stats->slabsize);
+	luaL_pushuint64(L, stats->slabsize);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "mem_free");
-	luaL_pushnumber64(L, stats->totals.total - stats->totals.used);
+	luaL_pushuint64(L, stats->totals.total - stats->totals.used);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "item_size");
-	luaL_pushnumber64(L, stats->objsize);
+	luaL_pushuint64(L, stats->objsize);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "slab_count");
-	luaL_pushnumber64(L, stats->slabcount);
+	luaL_pushuint64(L, stats->slabcount);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "item_count");
-	luaL_pushnumber64(L, stats->objcount);
+	luaL_pushuint64(L, stats->objcount);
 	lua_settable(L, -3);
 
 	lua_settable(L, -3);
@@ -107,11 +107,11 @@ lbox_slab_info(struct lua_State *L)
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "arena_used");
-	luaL_pushnumber64(L, totals.used);
+	luaL_pushuint64(L, totals.used);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "arena_size");
-	luaL_pushnumber64(L, totals.total);
+	luaL_pushuint64(L, totals.total);
 	lua_settable(L, -3);
 
 	char value[32];
@@ -140,11 +140,11 @@ lbox_runtime_info(struct lua_State *L)
 	lua_newtable(L);
 
 	lua_pushstring(L, "used");
-	luaL_pushnumber64(L, runtime.used);
+	luaL_pushuint64(L, runtime.used);
 	lua_settable(L, -3);
 
 	lua_pushstring(L, "maxalloc");
-	luaL_pushnumber64(L, quota_get(runtime.quota));
+	luaL_pushuint64(L, quota_get(runtime.quota));
 	lua_settable(L, -3);
 
 	return 1;
diff --git a/src/box/vclock.c b/src/box/vclock.c
index a01a21dbd4..6168ef1e0c 100644
--- a/src/box/vclock.c
+++ b/src/box/vclock.c
@@ -50,7 +50,7 @@ vclock_follow(struct vclock *vclock, uint32_t server_id, int64_t lsn)
 	return prev_lsn;
 }
 
-static inline __attribute__ ((format(FORMAT_PRINTF, 4, 0))) int
+static inline __attribute__ ((format(printf, 4, 0))) int
 rsnprintf(char **buf, char **pos, char **end, const char *fmt, ...)
 {
 	int rc = 0;
diff --git a/src/coeio.cc b/src/coeio.cc
index 3cc8b10ee0..e2cbc2f12c 100644
--- a/src/coeio.cc
+++ b/src/coeio.cc
@@ -27,12 +27,18 @@
  * SUCH DAMAGE.
  */
 #include "coeio.h"
-#include "fiber.h"
-#include "exception.h"
-#include <errno.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "fiber.h"
+#include "exception.h"
+#include "third_party/tarantool_ev.h"
 
 /*
  * Asynchronous IO Tasks (libeio wrapper).
@@ -146,7 +152,7 @@ coio_on_finish(eio_req *req)
 
 ssize_t
 coio_task(struct coio_task *task, coio_task_cb func,
-	   coio_task_timeout_cb on_timeout, ev_tstamp timeout)
+	  coio_task_timeout_cb on_timeout, double timeout)
 {
 	/* from eio.c: REQ() definition */
 	memset(&task->base, 0, sizeof(task->base));
@@ -296,7 +302,7 @@ getaddrinfo_free_cb(struct coio_task *ptr)
 int
 coio_getaddrinfo(const char *host, const char *port,
 		 const struct addrinfo *hints, struct addrinfo **res,
-		 ev_tstamp timeout)
+		 double timeout)
 {
 	int rc = EAI_SYSTEM;
 	int save_errno = 0;
diff --git a/src/coeio.h b/src/coeio.h
index d76b272a6f..09576d70ea 100644
--- a/src/coeio.h
+++ b/src/coeio.h
@@ -30,21 +30,11 @@
  */
 
 #include "trivia/config.h"
-#include "trivia/util.h"
 
-#include <stdbool.h>
-#include <stdint.h>
+#include <sys/types.h> /* ssize_t */
 #include <stdarg.h>
-#include <unistd.h>
-#include <coro.h>
-#include "third_party/tarantool_ev.h"
-#include "third_party/tarantool_eio.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
 
-#define ERESOLVE -1
+#include "third_party/tarantool_eio.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -89,15 +79,19 @@ struct coio_task {
 
 ssize_t
 coio_task(struct coio_task *task, coio_task_cb func,
-	  coio_task_timeout_cb on_timeout, ev_tstamp timeout);
+	  coio_task_timeout_cb on_timeout, double timeout);
 
+/** \cond public */
 ssize_t
 coio_call(ssize_t (*func)(va_list ap), ...);
 
+struct addrinfo;
+
 int
 coio_getaddrinfo(const char *host, const char *port,
 		 const struct addrinfo *hints, struct addrinfo **res,
-		 ev_tstamp timeout);
+		 double timeout);
+/** \endcond public */
 
 #if defined(__cplusplus)
 }
diff --git a/src/evio.cc b/src/evio.cc
index 211d473329..53884a803b 100644
--- a/src/evio.cc
+++ b/src/evio.cc
@@ -36,6 +36,8 @@
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 
+#include <trivia/util.h>
+
 #define BIND_RETRY_DELAY 0.1
 
 static void
diff --git a/src/fio.c b/src/fio.c
index 2edae2f054..2a184161f3 100644
--- a/src/fio.c
+++ b/src/fio.c
@@ -39,6 +39,7 @@
 #include <lib/bit/bit.h>
 
 #include <say.h>
+#include <trivia/util.h>
 
 const char *
 fio_filename(int fd)
diff --git a/src/fiob.c b/src/fiob.c
index b7840bf440..afbd7f0e13 100644
--- a/src/fiob.c
+++ b/src/fiob.c
@@ -17,6 +17,7 @@
 #include <assert.h>
 #include <unistd.h>
 #include <trivia/config.h>
+#include <trivia/util.h>
 
 /* Use special implemention if we have O_DIRECT and FOPENCOOKIE or FUNOPEN */
 #if defined(O_DIRECT) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
diff --git a/src/lib/small/region.h b/src/lib/small/region.h
index 11adb988b7..f53bead861 100644
--- a/src/lib/small/region.h
+++ b/src/lib/small/region.h
@@ -32,6 +32,7 @@
 #include <inttypes.h>
 #include <assert.h>
 #include <stdio.h>
+#include <string.h>
 #include "salad/rlist.h"
 #include "slab_cache.h"
 
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 96e998484b..e06b99970f 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -128,7 +128,7 @@ lbox_tonumber64(struct lua_State *L)
 		errno = 0;
 		unsigned long long result = strtoull(arg, &arge, 10);
 		if (errno == 0 && arge != arg) {
-			luaL_pushnumber64(L, result);
+			luaL_pushuint64(L, result);
 			return 1;
 		}
 		break;
diff --git a/src/lua/msgpack.cc b/src/lua/msgpack.cc
index c1b842dde6..8cff635fad 100644
--- a/src/lua/msgpack.cc
+++ b/src/lua/msgpack.cc
@@ -291,10 +291,10 @@ luamp_decode(struct lua_State *L, struct luaL_serializer *cfg,
 	double d;
 	switch (mp_typeof(**data)) {
 	case MP_UINT:
-		luaL_pushnumber64(L, mp_decode_uint(data));
+		luaL_pushuint64(L, mp_decode_uint(data));
 		break;
 	case MP_INT:
-		luaL_pushinumber64(L, mp_decode_int(data));
+		luaL_pushint64(L, mp_decode_int(data));
 		break;
 	case MP_FLOAT:
 		d = mp_decode_float(data);
diff --git a/src/lua/pickle.cc b/src/lua/pickle.cc
index 5b7d5e0af4..9666a2e343 100644
--- a/src/lua/pickle.cc
+++ b/src/lua/pickle.cc
@@ -209,12 +209,12 @@ lbox_unpack(struct lua_State *L)
 			break;
 		case 'l':
 			CHECK_SIZE(s + 7);
-			luaL_pushnumber64(L, *(uint64_t*) s);
+			luaL_pushuint64(L, *(uint64_t*) s);
 			s += 8;
 			break;
 		case 'q':
 			CHECK_SIZE(s + 7);
-			luaL_pushnumber64(L, bswap_u64(*(uint64_t*) s));
+			luaL_pushuint64(L, bswap_u64(*(uint64_t*) s));
 			s += 8;
 			break;
 		case 'd':
diff --git a/src/lua/utils.cc b/src/lua/utils.cc
index ffd0908c50..0f3b8be923 100644
--- a/src/lua/utils.cc
+++ b/src/lua/utils.cc
@@ -641,7 +641,7 @@ luaL_register_module(struct lua_State *L, const char *modname,
 }
 
 int
-luaL_pushnumber64(struct lua_State *L, uint64_t val)
+luaL_pushuint64(struct lua_State *L, uint64_t val)
 {
 	if (val < (1ULL << 52)) {
 		/* push lua_Number (double) */
@@ -655,7 +655,7 @@ luaL_pushnumber64(struct lua_State *L, uint64_t val)
 }
 
 int
-luaL_pushinumber64(struct lua_State *L, int64_t val)
+luaL_pushint64(struct lua_State *L, int64_t val)
 {
 	if (val > (-1LL << 52) && val < (1LL << 52)) {
 		/* push lua_Number (double) */
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 05119a885c..61194a5b42 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -52,6 +52,8 @@ extern "C" {
 
 struct lua_State;
 
+/** \cond public */
+
 /**
  * @brief Allocate a new block of memory with the given size, push onto the
  * stack a new cdata of type ctypeid with the block address, and return
@@ -63,7 +65,7 @@ struct lua_State;
  * @sa luaL_checkcdata
  * @return memory associated with this cdata
  */
-void *
+LUA_API void *
 luaL_pushcdata(struct lua_State *L, uint32_t ctypeid, uint32_t size);
 
 /**
@@ -74,7 +76,7 @@ luaL_pushcdata(struct lua_State *L, uint32_t ctypeid, uint32_t size);
  * @sa luaL_pushcdata
  * @return memory associated with this cdata
  */
-void *
+LUA_API void *
 luaL_checkcdata(struct lua_State *L, int idx, uint32_t *ctypeid);
 
 /**
@@ -85,7 +87,7 @@ luaL_checkcdata(struct lua_State *L, int idx, uint32_t *ctypeid);
  * @param idx object
  * @return 1
  */
-int
+LUA_API int
 luaL_setcdatagc(struct lua_State *L, int idx);
 
 /**
@@ -96,9 +98,11 @@ luaL_setcdatagc(struct lua_State *L, int idx);
 * @sa luaL_checkcdata
 * @return CTypeID
 */
-uint32_t
+LUA_API uint32_t
 luaL_ctypeid(struct lua_State *L, const char *ctypename);
 
+/** \endcond public */
+
 static inline lua_Integer
 luaL_arrlen(struct lua_State *L, int idx)
 {
@@ -352,6 +356,8 @@ void
 luaL_register_module(struct lua_State *L, const char *modname,
 		     const struct luaL_Reg *methods);
 
+/** \cond public */
+
 /**
  * push uint64_t to Lua stack
  *
@@ -359,12 +365,16 @@ luaL_register_module(struct lua_State *L, const char *modname,
  * @param val is a value to push
  *
  */
-int luaL_pushnumber64(struct lua_State *L, uint64_t val);
+LUA_API int
+luaL_pushuint64(struct lua_State *L, uint64_t val);
 
 /**
  * @copydoc luaL_pushnumber64
  */
-int luaL_pushinumber64(struct lua_State *L, int64_t val);
+LUA_API int
+luaL_pushint64(struct lua_State *L, int64_t val);
+
+/** \endcond public */
 
 /**
  * Push Lua Table with __serialize = 'map' hint onto the stack.
diff --git a/src/module/mysql/CMakeLists.txt b/src/module/mysql/CMakeLists.txt
index 3f5aa81354..28a155f733 100644
--- a/src/module/mysql/CMakeLists.txt
+++ b/src/module/mysql/CMakeLists.txt
@@ -5,6 +5,7 @@ if (MYSQL_FOUND)
 	add_library(mysql SHARED mysql.cc)
 	target_link_libraries(mysql ${MYSQL_LIBRARIES} -rdynamic)
 	set_target_properties(mysql PROPERTIES PREFIX "")
+	add_dependencies(mysql generate_module_api)
 	install(TARGETS mysql LIBRARY DESTINATION ${MODULE_LIBDIR}/box/net)
 else()
 	message(STATUS "MySQL client not found, box.net.sql(mysql) disabled")
diff --git a/src/module/mysql/mysql.cc b/src/module/mysql/mysql.cc
index 3bc120684a..2aa4845625 100644
--- a/src/module/mysql/mysql.cc
+++ b/src/module/mysql/mysql.cc
@@ -27,6 +27,8 @@
  * SUCH DAMAGE.
  */
 
+#include "module.h"
+
 #include <stddef.h>
 
 extern "C" {
@@ -39,14 +41,8 @@ extern "C" {
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
-#include <coeio.h>
-#include "third_party/tarantool_ev.h"
 
-#include <lua/init.h>
-#include <lua/utils.h>
-#include <say.h>
 #include <mysql.h>
-#include <scoped_guard.h>
 
 /**
  * gets MYSQL connector from lua stack (or object)
@@ -164,8 +160,8 @@ fetch_result(va_list ap)
 /**
  * push results to lua stack
  */
-int
-lua_push_mysql_result(struct lua_State *L, MYSQL *mysql,
+static int
+lua_mysql_pushresult(struct lua_State *L, MYSQL *mysql,
 	MYSQL_RES *result, int resno)
 {
 	int tidx;
@@ -187,6 +183,7 @@ lua_push_mysql_result(struct lua_State *L, MYSQL *mysql,
 			lua_pushnumber(L, v);
 			return 2;
 		}
+		mysql_free_result(result);
 		luaL_error(L, "%s", mysql_error(mysql));
 	}
 
@@ -225,7 +222,7 @@ lua_push_mysql_result(struct lua_State *L, MYSQL *mysql,
 				case MYSQL_TYPE_LONGLONG:
 				case MYSQL_TYPE_TIMESTAMP: {
 					long long v = atoll(row[i]);
-					luaL_pushnumber64(L, v);
+					luaL_pushuint64(L, v);
 					break;
 				}
 
@@ -249,11 +246,10 @@ lua_push_mysql_result(struct lua_State *L, MYSQL *mysql,
 	v += mysql_affected_rows(mysql);
 	lua_pop(L, 1);
 	lua_pushnumber(L, v);
+	mysql_free_result(result);
 	return 2;
 }
 
-
-
 /**
  * mysql:execute() method
  */
@@ -335,10 +331,7 @@ lua_mysql_execute(struct lua_State *L)
 		if (res == -1)
 			luaL_error(L, "%s", strerror(errno));
 
-		auto scope_guard = make_scoped_guard([&]{
-			mysql_free_result(result);
-		});
-		lua_push_mysql_result(L, mysql, result, resno++);
+		lua_mysql_pushresult(L, mysql, result, resno++);
 
 	} while(mysql_more_results(mysql));
 
diff --git a/src/module/pg/CMakeLists.txt b/src/module/pg/CMakeLists.txt
index 828aba9b8e..24aca28de8 100644
--- a/src/module/pg/CMakeLists.txt
+++ b/src/module/pg/CMakeLists.txt
@@ -6,6 +6,7 @@ if (PostgreSQL_FOUND)
 	add_library(pg SHARED pg.cc)
 	target_link_libraries(pg ${PostgreSQL_LIBRARIES} -rdynamic)
 	set_target_properties(pg PROPERTIES PREFIX "")
+	add_dependencies(pg generate_module_api)
 	install(TARGETS pg LIBRARY DESTINATION ${MODULE_LIBDIR}/box/net)
 else()
 	message(STATUS "PostgreSQL client not found, box.net.sql(pg) disabled")
diff --git a/src/module/pg/pg.cc b/src/module/pg/pg.cc
index b58ecb1761..a0c6a3ba69 100644
--- a/src/module/pg/pg.cc
+++ b/src/module/pg/pg.cc
@@ -27,6 +27,8 @@
  * SUCH DAMAGE.
  */
 
+#include "module.h"
+
 extern "C" {
 	#include <libpq-fe.h>
 	#undef PACKAGE_VERSION
@@ -52,12 +54,7 @@ extern "C" {
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
-#include <coeio.h>
-#include "third_party/tarantool_ev.h"
 
-#include <lua/init.h>
-#include <say.h>
-#include <scoped_guard.h>
 
 static PGconn *
 lua_check_pgconn(struct lua_State *L, int index)
@@ -103,7 +100,7 @@ pg_exec(va_list ap)
 
 /** push query result into lua stack */
 static int
-lua_push_pgres(struct lua_State *L, PGresult *r)
+lua_pg_pushresult(struct lua_State *L, PGresult *r)
 {
 	if (!r)
 		luaL_error(L, "PG internal error: zero rults");
@@ -120,21 +117,28 @@ lua_push_pgres(struct lua_State *L, PGresult *r)
 				lua_pushnumber(L, v);
 			}
 			lua_pushstring(L, PQcmdStatus(r));
+			PQclear(r);
 			return 3;
 
 		case PGRES_TUPLES_OK:
 			break;
 
 		case PGRES_BAD_RESPONSE:
+			PQclear(r);
 			luaL_error(L, "Broken postgresql response");
+			return 0;
 
 		case PGRES_FATAL_ERROR:
 		case PGRES_NONFATAL_ERROR:
 		case PGRES_EMPTY_QUERY:
-			luaL_error(L, "%s", PQresultErrorMessage(r));
+			lua_pushstring(L, PQresultErrorMessage(r));
+			PQclear(r);
+			luaL_error(L, "%s", lua_tostring(L, -1));
+			return 0;
 
 		default:
-			luaL_error(L, "box.net.sql.pg: internal error");
+			luaL_error(L, "pg: unsupported result type");
+			return 0;
 	}
 
 	lua_newtable(L);
@@ -192,6 +196,7 @@ lua_push_pgres(struct lua_State *L, PGresult *r)
 		lua_pushnumber(L, v);
 	}
 	lua_pushstring(L, PQcmdStatus(r));
+	PQclear(r);
 	return 3;
 }
 
@@ -290,11 +295,8 @@ lua_pg_execute(struct lua_State *L)
 			strerror(errno));
 	}
 
-	auto scope_guard = make_scoped_guard([&]{
-		PQclear(res);
-	});
 	lua_settop(L, 0);
-	return lua_push_pgres(L, res);
+	return lua_pg_pushresult(L, res);
 }
 
 /**
diff --git a/src/module_footer.h b/src/module_footer.h
new file mode 100644
index 0000000000..c5920a9f45
--- /dev/null
+++ b/src/module_footer.h
@@ -0,0 +1,5 @@
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_MODULE_H_INCLUDED */
diff --git a/src/module_header.h b/src/module_header.h
new file mode 100644
index 0000000000..4859be717b
--- /dev/null
+++ b/src/module_header.h
@@ -0,0 +1,20 @@
+#ifndef TARANTOOL_MODULE_H_INCLUDED
+#define TARANTOOL_MODULE_H_INCLUDED
+
+#include <stddef.h>
+#include <stdarg.h> /* va_list */
+#include <errno.h>
+#include <string.h> /* strerror(3) */
+#include <stdint.h>
+#include <sys/types.h> /* ssize_t */
+
+#include <lua.h>
+
+/**
+ * \file
+ * Tarantool Module API
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
diff --git a/src/say.h b/src/say.h
index b8132ef0f5..6a01587323 100644
--- a/src/say.h
+++ b/src/say.h
@@ -32,12 +32,12 @@
 #include <stdarg.h>
 #include <errno.h>
 
-#include "trivia/util.h" /* for FORMAT_PRINTF */
-
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+/** \cond public */
+
 enum say_level {
 	S_FATAL,		/* do not this value use directly */
 	S_SYSERROR,
@@ -48,6 +48,8 @@ enum say_level {
 	S_DEBUG
 };
 
+/** \endcond public */
+
 extern int log_fd;
 extern pid_t logger_pid;
 
@@ -66,12 +68,13 @@ void say_logger_init(const char *logger, int log_level, int nonblock,
 
 void vsay(int level, const char *filename, int line, const char *error,
           const char *format, va_list ap)
-          __attribute__ ((format(FORMAT_PRINTF, 5, 0)));
+          __attribute__ ((format(printf, 5, 0)));
 
+/** \cond public */
 typedef void (*sayfunc_t)(int level, const char *filename, int line, const char *error,
                           const char *format, ...);
 
-extern sayfunc_t _say __attribute__ ((format(FORMAT_PRINTF, 5, 6)));
+extern sayfunc_t _say __attribute__ ((format(printf, 5, 6)));
 
 #define say(level, ...) ({ _say(level, __FILE__, __LINE__, __VA_ARGS__); })
 
@@ -84,7 +87,7 @@ extern sayfunc_t _say __attribute__ ((format(FORMAT_PRINTF, 5, 6)));
 #define say_warn(...)			say(S_WARN, NULL, __VA_ARGS__)
 #define say_info(...)			say(S_INFO, NULL, __VA_ARGS__)
 #define say_debug(...)			say(S_DEBUG, NULL, __VA_ARGS__)
-
+/** \endcond public */
 
 #if defined(__cplusplus)
 } /* extern "C" */
diff --git a/src/sio.cc b/src/sio.cc
index 08eda96cda..8c981f041c 100644
--- a/src/sio.cc
+++ b/src/sio.cc
@@ -44,6 +44,7 @@
 #endif /* #ifdef TARGET_OS_LINUX */
 
 #include "say.h"
+#include "trivia/util.h"
 
 SocketError::SocketError(const char *file, unsigned line, int fd,
 			 const char *format, ...)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 547d6a50ee..466cceaf2d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -13,7 +13,7 @@ add_custom_target(test
 add_custom_target(test-force
     COMMAND ${PROJECT_SOURCE_DIR}/test/test-run.py --builddir=${PROJECT_BINARY_DIR} --force --vardir=${PROJECT_BINARY_DIR}/test/var)
 
-
+add_subdirectory(app)
 add_subdirectory(unit)
 # Disable connector_c for 1.6
 #add_subdirectory(connector_c)
diff --git a/test/app/CMakeLists.txt b/test/app/CMakeLists.txt
new file mode 100644
index 0000000000..1245fe14c4
--- /dev/null
+++ b/test/app/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(module_api SHARED module_api.c)
+target_link_libraries(module_api -rdynamic)
+set_target_properties(module_api PROPERTIES PREFIX "")
+add_dependencies(module_api generate_module_api)
diff --git a/test/app/module_api.c b/test/app/module_api.c
new file mode 100644
index 0000000000..36d52807fc
--- /dev/null
+++ b/test/app/module_api.c
@@ -0,0 +1,103 @@
+#include "module.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+
+static int
+test_say(lua_State *L)
+{
+	say_debug("test debug");
+	say_info("test info");
+	say_warn("test warn");
+	say_crit("test crit");
+	say_error("test error");
+	errno = 0;
+	say_syserror("test sysserror");
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+static ssize_t
+coio_call_func(va_list ap)
+{
+	return va_arg(ap, int);
+}
+
+static int
+test_coio_call(lua_State *L)
+{
+	ssize_t rc = coio_call(coio_call_func, 48);
+	lua_pushboolean(L, rc == 48);
+	return 1;
+}
+
+static int
+test_coio_getaddrinfo(lua_State *L)
+{
+	struct addrinfo hints;
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_ADDRCONFIG|AI_PASSIVE;
+	hints.ai_protocol = 0;
+	struct addrinfo *ai = NULL;
+	if (coio_getaddrinfo("localhost", "80", &hints, &ai, 0.1) == 0)
+		freeaddrinfo(ai);
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+static int
+test_pushcheck_cdata(lua_State *L)
+{
+	uint32_t uint64_ctypeid = luaL_ctypeid(L, "uint64_t");
+	*(uint64_t *) luaL_pushcdata(L, uint64_ctypeid, sizeof(uint64_t)) = 48;
+	uint32_t test_ctypeid = 0;
+	luaL_checkcdata(L, -1, &test_ctypeid);
+	lua_pushboolean(L, test_ctypeid != 0 && uint64_ctypeid == test_ctypeid);
+	return 1;
+}
+
+static int
+test_pushuint64(lua_State *L)
+{
+	uint32_t ctypeid = 0;
+	uint64_t num = 18446744073709551615ULL;
+	luaL_pushuint64(L, num);
+	uint64_t r = *(uint64_t *) luaL_checkcdata(L, -1, &ctypeid);
+	lua_pushboolean(L, r == num && ctypeid == luaL_ctypeid(L, "uint64_t"));
+	return 1;
+}
+
+static int
+test_pushint64(lua_State *L)
+{
+	uint32_t ctypeid = 0;
+	int64_t num = 9223372036854775807LL;
+	luaL_pushint64(L, num);
+	int64_t r = *(int64_t *) luaL_checkcdata(L, -1, &ctypeid);
+	lua_pushboolean(L, r == num && ctypeid == luaL_ctypeid(L, "int64_t"));
+	return 1;
+}
+
+LUA_API int
+luaopen_module_api(lua_State *L)
+{
+	static const struct luaL_reg lib[] = {
+		{"test_say", test_say },
+		{"test_coio_call", test_coio_call },
+		{"test_coio_getaddrinfo", test_coio_getaddrinfo },
+		{"test_pushcheck_cdata", test_pushcheck_cdata },
+		{"test_pushuint64", test_pushuint64 },
+		{"test_pushint64", test_pushint64 },
+
+		{NULL, NULL}
+	};
+	luaL_register(L, "module_api", lib);
+	return 1;
+}
diff --git a/test/app/module_api.result b/test/app/module_api.result
new file mode 100644
index 0000000000..38640458a6
--- /dev/null
+++ b/test/app/module_api.result
@@ -0,0 +1,11 @@
+TAP version 13
+# module_api
+1..7
+ok - module is loaded
+ok - test_coio_call is ok
+ok - test_pushint64 is ok
+ok - test_say is ok
+ok - test_pushuint64 is ok
+ok - test_pushcheck_cdata is ok
+ok - test_coio_getaddrinfo is ok
+# module_api: end
diff --git a/test/app/module_api.test.lua b/test/app/module_api.test.lua
new file mode 100755
index 0000000000..b8e50193b6
--- /dev/null
+++ b/test/app/module_api.test.lua
@@ -0,0 +1,20 @@
+#!/usr/bin/env tarantool
+
+box.cfg{logger = "tarantool.log"}
+
+local work_dir = require('fio').dirname(arg[0])
+package.cpath = work_dir..'/?.so;'
+
+local test = require('tap').test("module_api", function(test)
+    test:plan(7)
+    local status, module = pcall(require, 'module_api')
+    test:ok(status, "module is loaded")
+    if not status then
+        return
+    end
+
+    for name, fun in pairs(module) do
+        test:ok(fun, name .. " is ok")
+    end
+end)
+os.exit(0)
diff --git a/third_party/lua-cjson/lua_cjson.c b/third_party/lua-cjson/lua_cjson.c
index 88599082cf..1b18473b8d 100644
--- a/third_party/lua-cjson/lua_cjson.c
+++ b/third_party/lua-cjson/lua_cjson.c
@@ -946,10 +946,10 @@ static void json_process_value(lua_State *l, json_parse_t *json,
         lua_pushlstring(l, token->value.string, token->string_len);
         break;;
     case T_UINT:
-        luaL_pushnumber64(l, token->value.ival);
+        luaL_pushuint64(l, token->value.ival);
         break;;
     case T_INT:
-        luaL_pushinumber64(l, token->value.ival);
+        luaL_pushint64(l, token->value.ival);
         break;;
     case T_NUMBER:
         luaL_checkfinite(l, json->cfg, token->value.number);
diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc
index e3b4596407..0818cb4faa 100644
--- a/third_party/lua-yaml/lyaml.cc
+++ b/third_party/lua-yaml/lyaml.cc
@@ -224,12 +224,12 @@ static void load_scalar(struct lua_yaml_loader *loader) {
       char *endptr = NULL;
       long long ival = strtoll(str, &endptr, 10);
       if (endptr == str + length && ival != LLONG_MAX) {
-         luaL_pushinumber64(loader->L, ival);
+         luaL_pushint64(loader->L, ival);
          return;
       }
       unsigned long long uval = strtoull(str, &endptr, 10);
       if (endptr == str + length) {
-         luaL_pushnumber64(loader->L, uval);
+         luaL_pushuint64(loader->L, uval);
          return;
       }
       double dval = fpconv_strtod(str, &endptr);
-- 
GitLab