1/*
2 * Copyright 2008-2018 Aerospike, Inc.
3 *
4 * Portions may be licensed to Aerospike, Inc. under one or more contributor
5 * license agreements.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 * use this file except in compliance with the License. You may obtain a copy of
9 * the License at http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17
18#include <aerospike/as_rec.h>
19#include <aerospike/as_val.h>
20
21#include <aerospike/mod_lua_record.h>
22#include <aerospike/mod_lua_val.h>
23#include <aerospike/mod_lua_bytes.h>
24#include <aerospike/mod_lua_reg.h>
25#include <aerospike/mod_lua_list.h>
26
27#include "internal.h"
28
29/*******************************************************************************
30 * MACROS
31 ******************************************************************************/
32
33#define OBJECT_NAME "record"
34#define CLASS_NAME "Record"
35
36/*******************************************************************************
37 * FUNCTIONS
38 ******************************************************************************/
39
40/**
41 * Read the item at index and convert to a record
42 */
43as_rec * mod_lua_torecord(lua_State * l, int index) {
44 mod_lua_box * box = mod_lua_tobox(l, index, CLASS_NAME);
45 return (as_rec *) mod_lua_box_value(box);
46}
47
48/**
49 * Push a record on to the lua stack
50 */
51as_rec * mod_lua_pushrecord(lua_State * l, as_rec * r) {
52 // I am hoping the following is correct use of the free flag
53 mod_lua_box * box = mod_lua_pushbox(l, r->_.free ? MOD_LUA_SCOPE_LUA : MOD_LUA_SCOPE_HOST, r, CLASS_NAME);
54 return (as_rec *) mod_lua_box_value(box);
55}
56
57/**
58 * Get the user record from the stack at index
59 */
60static as_rec * mod_lua_checkrecord(lua_State * l, int index) {
61 mod_lua_box * box = mod_lua_checkbox(l, index, CLASS_NAME);
62 return (as_rec *) mod_lua_box_value(box);
63}
64
65/**
66 * Garbage collection
67 */
68static int mod_lua_record_gc(lua_State * l) {
69 mod_lua_freebox(l, 1, CLASS_NAME);
70 return 0;
71}
72
73/**
74 * Get a record ttl:
75 * record.ttl(r)
76 */
77static int mod_lua_record_ttl(lua_State * l) {
78 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
79 lua_pushinteger(l, as_rec_ttl(rec));
80 return 1;
81}
82
83/**
84 * Get a record's last update time:
85 * record.last_update_time(r)
86 */
87static int mod_lua_record_last_update_time(lua_State * l) {
88 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
89 lua_pushinteger(l, as_rec_last_update_time(rec));
90 return 1;
91}
92
93/**
94 * Get a record generation:
95 * record.gen(r)
96 */
97static int mod_lua_record_gen(lua_State * l) {
98 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
99 lua_pushinteger(l, as_rec_gen(rec));
100 return 1;
101}
102
103/**
104 * Get a record key:
105 * record.key(r)
106 */
107static int mod_lua_record_key(lua_State * l) {
108 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
109 as_val * value = (as_val *) as_rec_key(rec);
110 if ( value != NULL ) {
111 mod_lua_pushval(l, value);
112 as_val_destroy(value);
113 return 1;
114 }
115 else {
116 lua_pushnil(l);
117 return 1;
118 }
119}
120
121/**
122 * Get a set name:
123 * record.setname(r)
124 */
125static int mod_lua_record_setname(lua_State * l) {
126 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
127 lua_pushstring(l, as_rec_setname(rec));
128 return 1;
129}
130
131/**
132 * Get a record digest:
133 * record.digest(r)
134 */
135static int mod_lua_record_digest(lua_State * l) {
136 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
137 as_bytes * b = as_rec_digest(rec);
138 mod_lua_pushbytes(l, b);
139 return 1;
140}
141
142/**
143 * Get a record numbins:
144 * record.numbins(r)
145 */
146static int mod_lua_record_numbins(lua_State * l) {
147 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
148 lua_pushinteger(l, as_rec_numbins(rec));
149 return 1;
150}
151
152typedef struct {
153 lua_State * state;
154 int return_val;
155} bin_names_data;
156
157void bin_names_callback(char * bin_names, uint32_t nbins, uint16_t max_name_size, void * udata) {
158 bin_names_data * data = (bin_names_data *) udata;
159 lua_State * l = data->state;
160 lua_createtable(l, nbins, 0);
161 if (nbins == 1 && *bin_names == 0) { // single-bin case
162 lua_pushnil(l);
163 lua_rawseti(l, -2, 1);
164 }
165 else {
166 for (uint16_t i = 0; i < nbins; i++) {
167 lua_pushstring(l, &bin_names[i * max_name_size]);
168 lua_rawseti(l, -2, i + 1);
169 }
170 }
171}
172
173/**
174 * Get a table of a record's bin names:
175 * record.bin_names(r)
176 */
177static int mod_lua_record_bin_names(lua_State * l) {
178 as_rec * rec = (as_rec *) mod_lua_checkrecord(l, 1);
179 bin_names_data udata = {.state = l, .return_val = 0};
180
181 as_rec_bin_names(rec, bin_names_callback, (void *) &udata);
182
183 return 1;
184}
185
186/**
187 * Set a record time to live (ttl)
188 */
189static int mod_lua_record_set_ttl(lua_State * l) {
190 as_rec * rec = mod_lua_checkrecord(l, 1);
191
192 // Get the 2nd arg off the stack -- and process as ttl
193 uint32_t ttl = (uint32_t)luaL_optinteger(l, 2, 0);
194
195 // This function just sets up the arguments,
196 // The udf record method will do the real work.
197 as_rec_set_ttl( rec, ttl );
198
199 return 0;
200}
201
202/**
203 * Drop a record's key
204 */
205static int mod_lua_record_drop_key(lua_State * l) {
206 as_rec * rec = mod_lua_checkrecord(l, 1);
207
208 // This function just sets up the arguments,
209 // The udf record method will do the real work.
210 as_rec_drop_key( rec );
211
212 return 0;
213}
214
215/**
216 * Get a value from the named bin
217 */
218static int mod_lua_record_index(lua_State * l) {
219 mod_lua_box * box = mod_lua_checkbox(l, 1, CLASS_NAME);
220 as_rec * rec = (as_rec *) mod_lua_box_value(box);
221 const char * name = luaL_optstring(l, 2, 0);
222 if ( name != NULL ) {
223 as_val * value = (as_val *) as_rec_get(rec, name);
224 if ( value != NULL ) {
225 mod_lua_pushval(l, value);
226 return 1;
227 }
228 else {
229 lua_pushnil(l);
230 return 1;
231 }
232 }
233 else {
234 lua_pushnil(l);
235 return 1;
236 }
237}
238
239
240/**
241 * Set a value in the named bin
242 */
243static int mod_lua_record_newindex(lua_State * l) {
244 as_rec * rec = mod_lua_checkrecord(l, 1);
245 const char * name = luaL_optstring(l, 2, 0);
246 if ( name != NULL ) {
247 // reference to this value is created by mod_lua_toval
248 // then stashed in the record cache
249 as_val * value = (as_val *) mod_lua_toval(l, 3);
250 if ( value != NULL ) {
251 as_rec_set(rec, name, value);
252 }
253 else {
254 as_rec_remove(rec, name);
255 }
256 }
257 return 0;
258}
259
260/******************************************************************************
261 * OBJECT TABLE
262 *****************************************************************************/
263
264static const luaL_reg object_table[] = {
265 {"ttl", mod_lua_record_ttl},
266 {"last_update_time", mod_lua_record_last_update_time},
267 {"gen", mod_lua_record_gen},
268 {"key", mod_lua_record_key},
269 {"setname", mod_lua_record_setname},
270 {"digest", mod_lua_record_digest},
271 {"numbins", mod_lua_record_numbins},
272 {"set_ttl", mod_lua_record_set_ttl},
273 {"drop_key", mod_lua_record_drop_key},
274 {"bin_names", mod_lua_record_bin_names},
275 {0, 0}
276};
277
278static const luaL_reg object_metatable[] = {
279 // {"__index", mod_lua_record_index},
280 {0, 0}
281};
282
283/******************************************************************************
284 * CLASS TABLE
285 *****************************************************************************/
286/*
287static const luaL_reg class_table[] = {
288 {0, 0}
289};
290*/
291
292static const luaL_reg class_metatable[] = {
293 {"__index", mod_lua_record_index},
294 {"__newindex", mod_lua_record_newindex},
295 {"__gc", mod_lua_record_gc},
296 {0, 0}
297};
298
299/*******************************************************************************
300 * ~~~ Register ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301 ******************************************************************************/
302
303int mod_lua_record_register(lua_State * l) {
304 mod_lua_reg_object(l, OBJECT_NAME, object_table, object_metatable);
305 mod_lua_reg_class(l, CLASS_NAME, NULL, class_metatable);
306 return 1;
307}
308