├── .cproject ├── .gitignore ├── build_linux.sh ├── build_osx.sh ├── readme.md ├── src ├── .gitignore └── as_lua.c └── test └── main.lua /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /as_lua.so 2 | -------------------------------------------------------------------------------- /build_linux.sh: -------------------------------------------------------------------------------- 1 | gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/as_lua.d" -MT"src/as_lua.d" -o "./src/as_lua.o" "src/as_lua.c" -std=gnu99 -g -rdynamic -Wall -fno-common -fno-strict-aliasing -fPIC -DMARCH_x86_64 -D_FILE_OFFSET_BITS=64 -D_REENTRANT -D_GNU_SOURCE 2 | gcc -shared -o "as_lua.so" ./src/as_lua.o -laerospike -lssl -lcrypto -lpthread -lrt -lm 3 | -------------------------------------------------------------------------------- /build_osx.sh: -------------------------------------------------------------------------------- 1 | clang -std=gnu99 -g -Wall -fPIC -fno-common -fno-strict-aliasing -march=nocona -DMARCH_i386 -D_FILE_OFFSET_BITS=64 -D_REENTRANT -D_GNU_SOURCE -D_DARWIN_UNLIMITED_SELECT -I/usr/local/include -o src/as_lua.o -c src/as_lua.c 2 | clang -dynamiclib -o as_lua.so src/as_lua.o /usr/local/lib/libaerospike.a -lssl -lcrypto -lpthread -L/usr/local/lib -lm -lz -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Calling Aerospike from Lua 2 | ## Problem 3 | You are using [Lua](http://www.lua.org/) and would like to make calls to an Aerospike database. But Aerospike does not provide a Lua Client API. 4 | 5 | You may be using Lua within application servers like Nginx, BarracudaDrive, Mako, OpenResty, Kepler, etc - similar to how Javascript is used inside Node. 6 | ## Solution 7 | The solution is conceptually simple: Call the Aerospike C API from Lua. There is extensive Lua documentation and many books and blogs that describe calling C from Lua. This example demonstrates how to wrap the Aerospike C client API so it can be called from Lua. 8 | 9 | The source code for this solution is available on GitHub at 10 | http://github.com/aerospike/client-lua. 11 | 12 | The project also requires the Aerospike C client, which is available at http://github.com/aerospike/aerospike-client-c or from http://aerospike.com . You will need to build the client and have the Aerospike headers and libraries available in the search path. 13 | 14 | 15 | You’ll need the Lua development environment, and the ```lua.h``` in the include path. You can include the Lua 5.0 or 5.1 environment from the source, or install a Lua development package with your package manager. 16 | ### Build Instructions 17 | 18 | In this example, we will wrap 5 basic Aerospike key-value operations: 19 | 1. **Connect** - connect to an Aerospike cluster 20 | 2. **Get** - read a record from the Aerospike cluster 21 | 3. **Put** - write a record to the Aerospike cluster 22 | 4. **Increment** - increment a Bin value in a record 23 | 5. **Disconnect** - disconnect from the Aerospike cluster 24 | 25 | You can use this example to wrap other Aerospike operations in the C library. This C library must be installed on the Lua library path for Lua to find it. 26 | 27 | There is a lot of information at the [Lua users wiki](http://lua-users.org/wiki/) that describes calling C from Lua. Chapter 24 of “[Programming in Lua](http://www.lua.org/pil/)” describes in detail how this is done. 28 | 29 | 30 | Run the build script located in the root directory of the repository to build the library "as_lua.so" 31 | 32 | For Linux 33 | ``` 34 | ./build_linux.sh 35 | ``` 36 | For OS X 37 | ``` 38 | ./build_osx.sh 39 | ``` 40 | The shared library “as_lua.so” has dependencies on the Aerospike C Client API. Refer to the [Aerospike C Client](https://docs.aerospike.com/pages/viewpage.action?pageId=3807998) documentation for instructions on insallation and dependencies. 41 | 42 | 43 | **IMPORTANT:** The shared library as_lua.so should be placed in the Lua library path: 44 | ``` 45 | ./as_lua.so 46 | /usr/local/lib/lua/5.1/as_lua.so 47 | /usr/lib/lua/5.1/as_lua.so 48 | ``` 49 | ### Lua application to calling Aerospike 50 | 51 | This example includes a simple Lua program that exercises each function implemented in the library. You will find code in the file ```main.lua``` in the “test” subdirectory. 52 | 53 | Something to remember: 54 | This is a C library that calls the Aerospike 3 C Client API. 55 | This project has all the same dependencies as the Aerospike C Client API. It will only run on the same platforms supported by the C Client. 56 | 57 | 58 | ## Discussion 59 | The functions that will be exposed to Lua are defined in the following code: 60 | ```c 61 | static const struct luaL_Reg as_client [] = { 62 | {"connect", connect}, 63 | {"disconnect", disconnect}, 64 | {"get", get}, 65 | {"put", put}, 66 | {"increment", increment}, 67 | {NULL, NULL} 68 | }; 69 | 70 | extern int luaopen_as_lua(lua_State *L){ 71 | luaL_register(L, "as_lua", as_client); 72 | return 0; 73 | } 74 | ``` 75 | This function is called by the require statement in Lua. When require is called, Lua will look for a library named ```as_lua.so``` on its library path. Be sure to put ```as_lua.so``` in one of the following directories, depending on your Lua installation: 76 | ``` 77 | ./as_lua.so 78 | /usr/local/lib/lua/5.1/as_lua.so 79 | /usr/lib/lua/5.1/as_lua.so 80 | ``` 81 | The C function ```lua_open_aerospike``` is called by the Lua function require ```as_lua```. At the start of your Lua application, you should have code like this: 82 | ```lua 83 | local as = require "as_lua" 84 | ``` 85 | ### Connect 86 | To connect to an Aerospike cluster you need to supply one or more “seed nodes” and ports. When a client application connects to a cluster, one name or IP address and port is sufficient. The Aerospike intelligent client will discover all the nodes in the cluster and become a 1st class observer of them. Additional nodes can be supplied at connection time in case the node you have specified is actually down. 87 | 88 | Our Lua function will take one seed node address and port, and return a handle to a cluster. 89 | ```lua 90 | err, message, cluster = as.connect("localhost", 3000) 91 | print("connected", err, message) 92 | ``` 93 | Connect must be called before you call any other Aerospike function. 94 | Connect will return 3 values, in the above example, the variables ```err``` and ```message``` will contain the error code and error message, and the variable cluster will be a handle to the Aerospike cluster. You will use the cluster handle in subsequent Aerospike function calls. 95 | 96 | The C code to implement this function: 97 | ```c 98 | static int connect(lua_State *L){ 99 | const char *hostName = luaL_checkstring(L, 1); 100 | int port = lua_tointeger(L, 2); 101 | as_error err; 102 | 103 | // Configuration for the client. 104 | as_config config; 105 | as_config_init(&config); 106 | 107 | // Add a seed host for cluster discovery. 108 | as_config_add_host(&config, hostName, port); 109 | 110 | // The Aerospike client instance, initialized with the configuration. 111 | aerospike_init(&as, &config); 112 | 113 | // Connect to the cluster. 114 | aerospike_connect(&as, &err); 115 | 116 | /* Push the return */ 117 | lua_pushnumber(L, err.code); 118 | lua_pushstring(L, err.message); 119 | lua_pushlightuserdata(L, &as); 120 | return 3; 121 | } 122 | ``` 123 | Lua uses its own stack mechanism to pass parameters between Lua and C. You will note at the start of this function, parameters are taken from the stack 124 | ```c 125 | const char *hostName = luaL_checkstring(L, 1); 126 | int port = lua_tointeger(L, 2); 127 | ``` 128 | and at the end of the function, the return values are pushed onto the stack 129 | ```c 130 | lua_pushnumber(L, err.code); 131 | lua_pushstring(L, err.message); 132 | lua_pushlightuserdata(L, &as); 133 | return 3; 134 | ``` 135 | Note that there are 3 return values: 136 | - error number 137 | - error message 138 | - cluster pointer 139 | 140 | ### Disconnect 141 | When your Lua application has completed using Aerospike, usually at the end of the Lua code, it should disconnect from the Aerospike cluster. This frees resources in the process, things like socket connections, file descriptors, etc. 142 | 143 | Our Lua function to disconnect from the cluster will take one parameter that is the cluster handle. 144 | ```lua 145 | err, message = as.disconnect(cluster) 146 | print("disconnected", err, message) 147 | ``` 148 | The C code to implement this function: 149 | ```c 150 | static int disconnect(lua_State *L){ 151 | aerospike* as = lua_touserdata(L, 1); 152 | as_error err; 153 | aerospike_close(as, &err); 154 | lua_pushnumber(L, err.code); 155 | lua_pushstring(L, err.message); 156 | return 2; 157 | } 158 | ``` 159 | You should be seeing a pattern emerging in the way the parameters are passed to the function and how values are returned. The actual code that interacts with Aerospike is trivial. Most of the work in this function is to obtain the pointer to the cluster and return 2 values to Lua 160 | ### Get 161 | To read a record from Aerospike you use the ```get``` function. The cluster handle, obtained from the ```connect``` function, is the first argument and is the reference the ```get``` function uses to interact with the cluster. 162 | 163 | The next 3 parameters are ```namespace```, ```set``` and ```key```, in that order. These values uniquely identify the record. 164 | ```lua 165 | err, message, record = as.get(cluster, "test", "test", "peter001") 166 | print("read record", err, message, record.uid, record.name, record.dob) 167 | ``` 168 | The ```get``` function returns 3 values. The 1st is the error code, the second is the error message if one exists, the 3rd is a table containing the record values. 169 | 170 | You will note that this function reads all the Bins in the record and is the easiest to implement. A better implementation would offer the capability to read a subset of Bins. We will leave that activity up to the reader. 171 | 172 | ### Put 173 | To write data to Aerospike you use the ```put``` function. The cluster handle, obtained from the ```connect``` function, is the first argument and is the reference the ```put``` function uses to interact with the cluster. 174 | 175 | The next 3 parameters are ```namespace```, ```set``` and ```key```, in that order. These values uniquely identify the record. 176 | 177 | The 5th parameter is a table containing the Bin names and values. This is the actual data you want to store in Aerospike. 178 | ```lua 179 | local bins = {} 180 | bins["uid"] = "peter001" 181 | bins["name"] = "Peter" 182 | bins["dob"] = 19800101 183 | 184 | err, message = as.put(cluster, "test", "test", "peter001", 3, bins) 185 | print("saved record", err, message) 186 | ``` 187 | The put function returns 2 values. The 1st is the error code, the second is the error message if one exists. 188 | 189 | This function illustrates the complexity of passing parameters and return values between Lua and C. The complexity is in obtaining the values in the table parameter. The function ```add_bins_to_rec``` takes the passed-in table containing the Bin names and values and creates Bin values and populates a ```as_record``` structure. This structure is returned to the caller. 190 | 191 | The tricky part is obtaining the individual elements from the table. This function manipulates the Lua stack to iterate through the elements in the table and creates Bin values. 192 | ```c 193 | static as_record add_bins_to_rec(lua_State *L, int index, int numBins) 194 | { 195 | as_record rec; 196 | as_record_init(&rec, numBins); 197 | 198 | // Push another reference to the table on top of the stack (so we know 199 | // where it is, and this function can work for negative, positive and 200 | // pseudo indices 201 | lua_pushvalue(L, index); 202 | // stack now contains: -1 => table 203 | lua_pushnil(L); 204 | // stack now contains: -1 => nil; -2 => table 205 | while (lua_next(L, -2)) 206 | { 207 | // stack now contains: -1 => value; -2 => key; -3 => table 208 | // copy the key so that lua_tostring does not modify the original 209 | lua_pushvalue(L, -2); 210 | // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table 211 | const char *key = lua_tostring(L, -1); 212 | 213 | // add to record 214 | if (lua_isnumber(L, -2)){ 215 | int intValue = lua_tointeger(L, -2); 216 | as_record_set_int64(&rec, key, intValue); 217 | 218 | } else if (lua_isstring(L, -2)){ 219 | const char *value = lua_tostring(L, -2); 220 | as_record_set_str(&rec, key, value); 221 | } 222 | // pop value + copy of key, leaving original key 223 | lua_pop(L, 2); 224 | // stack now contains: -1 => key; -2 => table 225 | } 226 | 227 | // stack now contains: -1 => table (when lua_next returns 0 it pops the key 228 | // but does not push anything.) 229 | // Pop table 230 | lua_pop(L, 1); 231 | // Stack is now the same as it was on entry to this function 232 | return rec; 233 | } 234 | ``` 235 | ### Increment 236 | The increment function is quite similar to the put function in that it changes the value of one or more Bins in a record. It is useful when your application uses counters, and it also removes the need to read the value, increment the value in your application and rewrite it. 237 | ```lua 238 | bins = {} 239 | bins["counter"] = 1 240 | err, message = as.increment(cluster, "test", "test", "peter003", 1, bins) 241 | print("incremented record", err, message) 242 | ``` 243 | 244 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | /as_lua.d 2 | /as_lua.o 3 | -------------------------------------------------------------------------------- /src/as_lua.c: -------------------------------------------------------------------------------- 1 | 2 | #include /* Always include this */ 3 | #include /* Always include this */ 4 | #include /* Always include this */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // Allow only one global aerospike instance. 26 | aerospike as; 27 | 28 | static as_record add_bins_to_rec(lua_State *L, int index, int numBins) 29 | { 30 | as_record rec; 31 | as_record_init(&rec, numBins); 32 | 33 | // Push another reference to the table on top of the stack (so we know 34 | // where it is, and this function can work for negative, positive and 35 | // pseudo indices 36 | lua_pushvalue(L, index); 37 | // stack now contains: -1 => table 38 | lua_pushnil(L); 39 | // stack now contains: -1 => nil; -2 => table 40 | while (lua_next(L, -2)) 41 | { 42 | // stack now contains: -1 => value; -2 => key; -3 => table 43 | // copy the key so that lua_tostring does not modify the original 44 | lua_pushvalue(L, -2); 45 | // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table 46 | const char *binName = lua_tostring(L, -1); 47 | 48 | // add to record 49 | if (lua_isnumber(L, -2)){ 50 | int intValue = lua_tointeger(L, -2); 51 | as_record_set_int64(&rec, binName, intValue); 52 | 53 | } else if (lua_isstring(L, -2)){ 54 | const char *value = lua_tostring(L, -2); 55 | as_record_set_str(&rec, binName, value); 56 | } else if (lua_istable(L, -2)){ 57 | // make a as_list and populate it 58 | as_arraylist *list = as_arraylist_new(3, 3); 59 | 60 | lua_pushvalue(L, -2); 61 | lua_pushnil(L); 62 | // This is needed for it to even get the first value 63 | while (lua_next(L, -2)) 64 | { 65 | lua_pushvalue(L, -2); 66 | //const char *key = lua_tostring(L, -1); 67 | const char *value = lua_tostring(L, -2); 68 | // populate the as_list 69 | as_arraylist_append_str(list, value); 70 | //printf("%s => %s\n", key, value); 71 | lua_pop(L, 2); 72 | } 73 | lua_pop(L, 1); 74 | 75 | // put the list in a bin 76 | as_record_set_list(&rec, binName, (as_list*)as_val_reserve(list)); 77 | } 78 | // pop value + copy of key, leaving original key 79 | lua_pop(L, 2); 80 | // stack now contains: -1 => key; -2 => table 81 | } 82 | 83 | // stack now contains: -1 => table (when lua_next returns 0 it pops the key 84 | // but does not push anything.) 85 | // Pop table 86 | lua_pop(L, 1); 87 | // Stack is now the same as it was on entry to this function 88 | return rec; 89 | } 90 | 91 | 92 | 93 | static as_operations add_bins_to_increment(lua_State *L, int index, int numBins) 94 | { 95 | as_operations ops; 96 | as_operations_init(&ops, numBins); 97 | 98 | // Push another reference to the table on top of the stack (so we know 99 | // where it is, and this function can work for negative, positive and 100 | // pseudo indices 101 | lua_pushvalue(L, index); 102 | // stack now contains: -1 => table 103 | lua_pushnil(L); 104 | // stack now contains: -1 => nil; -2 => table 105 | while (lua_next(L, -2)) 106 | { 107 | // stack now contains: -1 => value; -2 => key; -3 => table 108 | // copy the key so that lua_tostring does not modify the original 109 | lua_pushvalue(L, -2); 110 | // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table 111 | const char *binName = lua_tostring(L, -1); 112 | int intValue = lua_tointeger(L, -2); 113 | 114 | //printf("Bin:%s, value:%d\n", binName, intValue); 115 | 116 | //add an operation for each bin 117 | as_operations_add_incr(&ops, binName, intValue); 118 | // pop value + copy of key, leaving original key 119 | lua_pop(L, 2); 120 | // stack now contains: -1 => key; -2 => table 121 | } 122 | 123 | // stack now contains: -1 => table (when lua_next returns 0 it pops the key 124 | // but does not push anything.) 125 | // Pop table 126 | lua_pop(L, 1); 127 | // Stack is now the same as it was on entry to this function 128 | return ops; 129 | } 130 | 131 | 132 | static int connect(lua_State *L){ 133 | const char *hostName = luaL_checkstring(L, 1); 134 | int port = lua_tointeger(L, 2); 135 | as_error err; 136 | 137 | // Configuration for the client. 138 | as_config config; 139 | as_config_init(&config); 140 | 141 | // Add a seed host for cluster discovery. 142 | //config.hosts[0].addr = hostName; 143 | //config.hosts[0].port = port; 144 | as_config_add_host(&config, hostName, port); 145 | 146 | // The Aerospike client instance, initialized with the configuration. 147 | aerospike_init(&as, &config); 148 | 149 | // Connect to the cluster. 150 | aerospike_connect(&as, &err); 151 | 152 | /* Push the return */ 153 | lua_pushnumber(L, err.code); 154 | lua_pushstring(L, err.message); 155 | lua_pushlightuserdata(L, &as); 156 | return 3; 157 | } 158 | 159 | static int disconnect(lua_State *L){ 160 | aerospike* as = lua_touserdata(L, 1); 161 | as_error err; 162 | aerospike_close(as, &err); 163 | lua_pushnumber(L, err.code); 164 | lua_pushstring(L, err.message); 165 | return 2; 166 | } 167 | static int get(lua_State *L){ 168 | //printf("-get-\n"); 169 | aerospike* as = lua_touserdata(L, 1); 170 | const char* nameSpace = luaL_checkstring(L, 2); 171 | const char* set = luaL_checkstring(L, 3); 172 | const char* keyString = luaL_checkstring(L, 4); 173 | //printf("key-:%s\n", keyString); 174 | as_record* rec = NULL; 175 | as_key key; 176 | as_error err; 177 | as_key_init(&key, nameSpace, set, keyString); 178 | 179 | // Read the test record from the database. 180 | aerospike_key_get(as, &err, NULL, &key, &rec); 181 | 182 | // Push the error code 183 | lua_pushnumber(L, err.code); 184 | 185 | // Push the error message 186 | lua_pushstring(L, err.message); 187 | 188 | // Create an new table and push it 189 | if ( err.code == AEROSPIKE_OK){ 190 | 191 | lua_newtable(L); /* create table to hold Bins read */ 192 | /* 193 | * iterate through bin and add the bin name 194 | * and value to the table 195 | */ 196 | as_record_iterator it; 197 | as_record_iterator_init(&it, rec); 198 | 199 | while (as_record_iterator_has_next(&it)) { 200 | as_bin *bin = as_record_iterator_next(&it); 201 | as_val *value = (as_val*)as_bin_get_value(bin); 202 | char * binName = as_bin_get_name(bin); 203 | 204 | int bin_type = as_val_type(value); //Bin Type 205 | 206 | switch (bin_type){ 207 | case AS_INTEGER: 208 | 209 | //printf("--integer-%s-\n", binName); 210 | lua_pushstring(L, binName); //Bin name 211 | lua_pushnumber(L, as_integer_get(as_integer_fromval(value))); 212 | //printf("--integer-end-\n"); 213 | break; 214 | case AS_DOUBLE: 215 | 216 | //printf("--double-%s-\n", binName); 217 | lua_pushstring(L, binName); //Bin name 218 | lua_pushnumber(L, as_double_get(as_double_fromval(value))); 219 | //printf("--double-end-\n"); 220 | break; 221 | case AS_STRING: 222 | //printf("--string-%s-\n", binName); 223 | lua_pushstring(L, binName); //Bin name 224 | lua_pushstring(L, as_val_tostring(value)); 225 | //printf("--string-end-\n"); 226 | break; 227 | case AS_LIST: 228 | //printf("--list-%s-\n", binName); 229 | lua_pushstring(L, binName); //Bin name 230 | // Iterate through arraylist populating table 231 | as_list* p_list = as_list_fromval(value); 232 | as_arraylist_iterator it; 233 | as_arraylist_iterator_init(&it, (const as_arraylist*)p_list); 234 | 235 | // create a Lua inner table table for the "List" 236 | lua_newtable(L); 237 | 238 | int count = 0; 239 | // See if the elements match what we expect. 240 | while (as_arraylist_iterator_has_next(&it)) { 241 | const as_val* p_val = as_arraylist_iterator_next(&it); 242 | //Assume string 243 | char* p_str = as_val_tostring(p_val); 244 | lua_pushnumber(L, count); // table[i] 245 | lua_pushstring(L, p_str); //Value 246 | //printf("%d => %s\n", count, p_str); 247 | count++; 248 | lua_settable(L, -3); 249 | } 250 | //printf("--list-end-\n"); 251 | break; 252 | } 253 | //printf("--settable-\n"); 254 | lua_settable(L, -3); 255 | //printf("--settable-end-\n"); 256 | } 257 | } 258 | as_record_destroy(rec); 259 | as_key_destroy(&key); 260 | //printf("-get-end-\n"); 261 | return 3; 262 | } 263 | 264 | 265 | 266 | static int put(lua_State *L){ 267 | 268 | //Cluster 269 | aerospike* as = lua_touserdata(L, 1); 270 | 271 | //Namespace 272 | const char* nameSpace = luaL_checkstring(L, 2); 273 | 274 | //Set 275 | const char* set = luaL_checkstring(L, 3); 276 | 277 | //Key as string 278 | const char* keyString = luaL_checkstring(L, 4); 279 | 280 | // Number of bins. 281 | const int numBins = lua_tointeger(L, 5); 282 | 283 | //Bins 284 | as_record rec = add_bins_to_rec(L, 6, numBins); 285 | 286 | //const as_record * test = &rec; 287 | //if (as_val_type(as_record_get(test, "animals")) == AS_LIST) 288 | // printf("correct list\n"); 289 | //else 290 | // printf("not a list\n"); 291 | 292 | // Create key 293 | as_key key; 294 | as_error err; 295 | as_key_init(&key, nameSpace, set, keyString); 296 | 297 | // Write record 298 | aerospike_key_put(as, &err, NULL, &key, &rec); 299 | as_key_destroy(&key); 300 | as_record_destroy(&rec); 301 | // Return status 302 | lua_pushnumber(L, err.code); 303 | lua_pushstring(L, err.message); 304 | return 2; 305 | 306 | } 307 | 308 | 309 | static int increment(lua_State *L){ 310 | as_error err; 311 | aerospike* as = lua_touserdata(L, 1); 312 | const char* nameSpace = luaL_checkstring(L, 2); 313 | const char* set = luaL_checkstring(L, 3); 314 | const char* keyString = luaL_checkstring(L, 4); 315 | const int numBins = lua_tointeger(L, 5); 316 | 317 | as_operations ops = add_bins_to_increment(L, 6, numBins); 318 | 319 | as_key key; 320 | as_key_init(&key, nameSpace, set, keyString); 321 | // Apply the operations. Since the record does not exist, it will be created 322 | // and the bins initialized with the ops' integer values. 323 | aerospike_key_operate(as, &err, NULL, &key, &ops, NULL); 324 | 325 | as_operations_destroy(&ops); 326 | as_key_destroy(&key); 327 | 328 | lua_pushnumber(L, err.code); 329 | lua_pushstring(L, err.message); 330 | return 2; 331 | } 332 | 333 | static const struct luaL_Reg as_client [] = { 334 | {"connect", connect}, 335 | {"disconnect", disconnect}, 336 | {"get", get}, 337 | {"put", put}, 338 | {"increment", increment}, 339 | {NULL, NULL} 340 | }; 341 | 342 | extern int luaopen_as_lua(lua_State *L){ 343 | luaL_register(L, "as_lua", as_client); 344 | return 0; 345 | } 346 | 347 | -------------------------------------------------------------------------------- /test/main.lua: -------------------------------------------------------------------------------- 1 | 2 | local as = require "as_lua" 3 | 4 | function tprint (tbl, indent) 5 | if not indent then indent = 0 end 6 | for k, v in pairs(tbl) do 7 | formatting = string.rep(" ", indent) .. k .. ": " 8 | if type(v) == "table" then 9 | print(formatting) 10 | tprint(v, indent+1) 11 | else 12 | print(formatting .. v) 13 | end 14 | end 15 | end 16 | 17 | 18 | local function main() 19 | local record 20 | local err 21 | local message 22 | local cluster 23 | 24 | err, message, cluster = as.connect("127.0.0.1", 3000) 25 | 26 | if err ~= 0 then 27 | print(err, message) 28 | return 29 | end 30 | 31 | print("## connected ##") 32 | 33 | local bins = {} 34 | bins["uid"] = "peter001" 35 | bins["name"] = "Peter" 36 | bins["dob"] = 19800101 37 | 38 | err, message = as.put(cluster, "test", "test", "peter001", 3, bins) 39 | print("saved record peter001", err, message) 40 | 41 | err, message, record = as.get(cluster, "test", "test", "peter001") 42 | print("read record peter001", err, message) 43 | tprint(record, 1) 44 | 45 | bins = {} 46 | bins["uid"] = "peter002" 47 | bins["name"] = "Peter2" 48 | bins["dob"] = 19800102 49 | 50 | err, message = as.put(cluster, "test", "test", "peter002", 3, bins) 51 | print("saved record peter002", err, message) 52 | 53 | bins = {} 54 | bins["uid"] = "peter004" 55 | bins["name"] = "Peter4" 56 | bins["dob"] = 19800102 57 | bins["animals"] = {"cats","mice","dogs","elephants","snakes"} 58 | 59 | err, message = as.put(cluster, "test", "test", "peter004", 4, bins) 60 | print("saved record peter004", err, message) 61 | 62 | bins = {} 63 | bins["counter"] = 1 64 | err, message = as.increment(cluster, "test", "test", "peter003", 1, bins) 65 | print("incremented record", err, message) 66 | 67 | err, message, record = as.get(cluster, "test", "test", "peter003") 68 | print("read record", err, message) 69 | tprint(record, 1) 70 | 71 | err, message, record = as.get(cluster, "test", "test", "peter004") 72 | print("read record", err, message) 73 | tprint(record, 1) 74 | 75 | bins = {} 76 | bins["uid"] = "peter005" 77 | bins["name"] = "Peter5" 78 | bins["dob"] = 19800102 79 | bins["animals"] = {"cats","mice","dogs","elephants","snakes"} 80 | bins["weight"] = 72.5 81 | 82 | err, message = as.put(cluster, "test", "test", "peter005", 5, bins) 83 | print("saved record peter005", err, message) 84 | 85 | err, message, record = as.get(cluster, "test", "test", "peter005") 86 | print("read record", err, message) 87 | tprint(record, 1) 88 | 89 | err, message = as.disconnect(cluster) 90 | print("disconnected", err, message) 91 | 92 | end 93 | main() 94 | --------------------------------------------------------------------------------