]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/lua: allow metatable fields to be cached 52225/head
authorYuval Lifshitz <ylifshit@redhat.com>
Thu, 22 Jun 2023 09:06:21 +0000 (09:06 +0000)
committerYuval Lifshitz <ylifshit@redhat.com>
Wed, 9 Aug 2023 14:30:16 +0000 (14:30 +0000)
this is by making metatable names fully qualified names
that contain the entire "path" for reaching them and not
just the name of the object they point to.

with the fix, the code would either create a new metatable,
as in this case:

local o1 = Request.Object
-- new metatable is created to represent the Object in Request.Object
local o2 = Request.CopyFrom.Object
-- new metatable (with different upvalues) is created to represent Request.CopyFrom.Object
print(o1.Name)
print(o2.Name)

or, will reuse an existing metatable, as in this case:

local o1 = Request.Object
-- new metatable is created to represent the Object in Request.Object
local o2 = Request.Object
-- reuse the same metatable
print(o1.Name)
print(o2.Name)

Fixes: https://tracker.ceph.com/issues/58412
Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
src/rgw/rgw_lua_background.cc
src/rgw/rgw_lua_background.h
src/rgw/rgw_lua_data_filter.cc
src/rgw/rgw_lua_request.cc
src/rgw/rgw_lua_utils.h
src/test/rgw/test_rgw_lua.cc

index 08e966ac44292e1d1e2842c053818dba5952ce1a..16a6ca5b4d81ea356c4d28d2c38524959ea63c19 100644 (file)
@@ -184,7 +184,10 @@ void Background::run() {
 }
 
 void Background::create_background_metatable(lua_State* L) {
-  create_metatable<rgw::lua::RGWTable>(L, true, &rgw_map, &table_mutex);
+  static const char* background_table_name = "RGW";
+  create_metatable<RGWTable>(L, "", background_table_name, true, &rgw_map, &table_mutex);
+  lua_getglobal(L, background_table_name);
+  ceph_assert(lua_istable(L, -1));
 }
 
 } //namespace rgw::lua
index bc967a7bde5be68b73677fb8340f1a60778f6171..dbca399a6957d6a7a328f441f3cbdc0f9c84b970 100644 (file)
@@ -21,14 +21,12 @@ struct RGWTable : EmptyMetaTable {
   static const char* INCREMENT;
   static const char* DECREMENT;
 
-  static std::string TableName() {return "RGW";}
-  static std::string Name() {return TableName() + "Meta";}
-  
   static int increment_by(lua_State* L);
 
   static int IndexClosure(lua_State* L) {
-    const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    std::ignore = table_name_upvalue(L);
+    const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(THIRD_UPVAL)));
     const char* index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, INCREMENT) == 0) {
@@ -69,8 +67,9 @@ struct RGWTable : EmptyMetaTable {
   }
 
   static int NewIndexClosure(lua_State* L) {
-    const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    auto& mtx = *reinterpret_cast<std::mutex*>(lua_touserdata(L, lua_upvalueindex(THIRD_UPVAL)));
     const auto index = luaL_checkstring(L, 2);
     
     if (strcasecmp(index, INCREMENT) == 0 || strcasecmp(index, DECREMENT) == 0) {
@@ -88,7 +87,7 @@ struct RGWTable : EmptyMetaTable {
         // erase the element. since in lua: "t[index] = nil" is removing the entry at "t[index]"
         if (const auto it = map->find(index); it != map->end()) {
           // index was found
-          update_erased_iterator<BackgroundMap>(L, it, map->erase(it));
+          update_erased_iterator<BackgroundMap>(L, name, it, map->erase(it));
         }
         return NO_RETURNVAL;
       case LUA_TBOOLEAN:
@@ -129,13 +128,7 @@ struct RGWTable : EmptyMetaTable {
   }
 
   static int PairsClosure(lua_State* L) {
-    auto map = reinterpret_cast<BackgroundMap*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    lua_pushlightuserdata(L, map);
-    lua_pushcclosure(L, next<BackgroundMap>, ONE_UPVAL); // push the stateless iterator function
-    lua_pushnil(L);                                         // indicate this is the first call
-    // return next(), nil
-
-    return TWO_RETURNVALS;
+    return Pairs<BackgroundMap>(L);
   }
 };
 
index 035c2401bfb0cc49c053bbd5f5a1fd76b9399665..a212faeb7787147b79ee668f44968568ae43b15d 100644 (file)
@@ -14,12 +14,9 @@ void push_bufferlist_byte(lua_State* L, bufferlist::iterator& it) {
 }
 
 struct BufferlistMetaTable : public EmptyMetaTable {
-
-  static std::string TableName() {return "Data";}
-  static std::string Name() {return TableName() + "Meta";}
-  
   static int IndexClosure(lua_State* L) {
-    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));
+    std::ignore = table_name_upvalue(L);
+    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     const auto index = luaL_checkinteger(L, 2);
     if (index <= 0 || index > bl->length()) {
       // lua arrays start from 1
@@ -37,19 +34,12 @@ struct BufferlistMetaTable : public EmptyMetaTable {
   }
 
   static int PairsClosure(lua_State* L) {
-    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(bl);
-    lua_pushlightuserdata(L, bl);
-    lua_pushcclosure(L, stateless_iter, ONE_UPVAL); // push the stateless iterator function
-    lua_pushnil(L);                                 // indicate this is the first call
-    // return stateless_iter, nil
-
-    return TWO_RETURNVALS;
+    return Pairs<bufferlist, stateless_iter>(L);
   }
   
   static int stateless_iter(lua_State* L) {
-    // based on: http://lua-users.org/wiki/GeneralizedPairsAndIpairs
-    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(1)));
+    std::ignore = table_name_upvalue(L);
+    auto bl = reinterpret_cast<bufferlist*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     lua_Integer index;
     if (lua_isnil(L, -1)) {
       index = 1;
@@ -90,15 +80,15 @@ int RGWObjFilter::execute(bufferlist& bl, off_t offset, const char* op_name) con
     ldpp_dout(s, 1) << "Failed to create state for Lua data context" << dendl;
     return -ENOMEM;
   }
-
   try {
     open_standard_libs(L);
 
     create_debug_action(L, s->cct);  
 
     // create the "Data" table
-    create_metatable<BufferlistMetaTable>(L, true, &bl);
-    lua_getglobal(L, BufferlistMetaTable::TableName().c_str());
+    static const char* data_metatable_name = "Data";
+    create_metatable<BufferlistMetaTable>(L, "", data_metatable_name, true, &bl);
+    lua_getglobal(L, data_metatable_name);
     ceph_assert(lua_istable(L, -1));
 
     // create the "Request" table
@@ -111,8 +101,6 @@ int RGWObjFilter::execute(bufferlist& bl, off_t offset, const char* op_name) con
     if (s->penv.lua.background) {
       // create the "RGW" table
       s->penv.lua.background->create_background_metatable(L);
-      lua_getglobal(L, rgw::lua::RGWTable::TableName().c_str());
-      ceph_assert(lua_istable(L, -1));
     }
 
     // execute the lua script
index 36fef1c82579e3d2f93525ae172b583ef4896d90..689624e80992b0843525c843e25dc24bcbeb3bcb 100644 (file)
@@ -117,11 +117,9 @@ int AddEvent(lua_State* L)  {
 }
 
 struct ResponseMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Response";} 
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto err = reinterpret_cast<const rgw_err*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto err = reinterpret_cast<const rgw_err*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -134,13 +132,14 @@ struct ResponseMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "Message") == 0) {
       pushstring(L, err->message);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
   
   static int NewIndexClosure(lua_State* L) {
-    auto err = reinterpret_cast<rgw_err*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    auto err = reinterpret_cast<rgw_err*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -153,18 +152,16 @@ struct ResponseMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "Message") == 0) {
       err->message.assign(luaL_checkstring(L, 3));
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return NO_RETURNVAL;
   }
 };
 
 struct QuotaMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Quota";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto info = reinterpret_cast<RGWQuotaInfo*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto info = reinterpret_cast<RGWQuotaInfo*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -177,18 +174,16 @@ struct QuotaMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "Rounded") == 0) {
       lua_pushboolean(L, !info->check_on_raw);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct PlacementRuleMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "PlacementRule";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto rule = reinterpret_cast<rgw_placement_rule*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto rule = reinterpret_cast<rgw_placement_rule*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -197,18 +192,16 @@ struct PlacementRuleMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "StorageClass") == 0) {
       pushstring(L, rule->storage_class);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct UserMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "User";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto user = reinterpret_cast<const rgw_user*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto user = reinterpret_cast<const rgw_user*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -217,18 +210,16 @@ struct UserMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "Id") == 0) {
       pushstring(L, user->id);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct TraceMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Trace";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -241,51 +232,49 @@ struct TraceMetaTable : public EmptyMetaTable {
         lua_pushlightuserdata(L, s);
         lua_pushcclosure(L, AddEvent, ONE_UPVAL);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 
   static int NewIndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Enable") == 0) {
       s->trace_enabled = lua_toboolean(L, 3);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return NO_RETURNVAL;
   }
 };
 
 struct OwnerMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Owner";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto owner = reinterpret_cast<ACLOwner*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto owner = reinterpret_cast<ACLOwner*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "DisplayName") == 0) {
       pushstring(L, owner->get_display_name());
     } else if (strcasecmp(index, "User") == 0) {
-      create_metatable<UserMetaTable>(L, false, &(owner->get_id()));
+      create_metatable<UserMetaTable>(L, name, index, false, 
+          &(owner->get_id()));
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct BucketMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Bucket";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     const auto bucket = s->bucket.get();
 
     const char* index = luaL_checkstring(L, 2);
@@ -315,20 +304,21 @@ struct BucketMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "MTime") == 0) {
       pushtime(L, bucket->get_modification_time());
     } else if (strcasecmp(index, "Quota") == 0) {
-      create_metatable<QuotaMetaTable>(L, false, &(bucket->get_info().quota));
+      create_metatable<QuotaMetaTable>(L, name, index, false, &(bucket->get_info().quota));
     } else if (strcasecmp(index, "PlacementRule") == 0) {
-      create_metatable<PlacementRuleMetaTable>(L, false, &(bucket->get_info().placement_rule));
+      create_metatable<PlacementRuleMetaTable>(L, name, index, false, &(bucket->get_info().placement_rule));
     } else if (strcasecmp(index, "User") == 0) {
-      create_metatable<UserMetaTable>(L, false, 
+      create_metatable<UserMetaTable>(L, name, index, false, 
           const_cast<rgw_user*>(&bucket->get_owner()->get_id()));
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
   
   static int NewIndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     const auto bucket = s->bucket.get();
 
     const char* index = luaL_checkstring(L, 2);
@@ -339,18 +329,16 @@ struct BucketMetaTable : public EmptyMetaTable {
         return NO_RETURNVAL;
       }
     }
-    return error_unknown_field(L, index, TableName());
+    return error_unknown_field(L, index, name);
   }
 };
 
 struct ObjectMetaTable : public EmptyMetaTable {
-  static const std::string TableName() {return "Object";}
-  static std::string Name() {return TableName() + "Meta";}
-  
   using Type = rgw::sal::Object;
 
   static int IndexClosure(lua_State* L) {
-    const auto obj = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto obj = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -365,18 +353,16 @@ struct ObjectMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "MTime") == 0) {
       pushtime(L, obj->get_mtime());
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct GrantMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Grant";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto grant = reinterpret_cast<ACLGrant*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto grant = reinterpret_cast<ACLGrant*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -385,7 +371,8 @@ struct GrantMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "User") == 0) {
       const auto id_ptr = grant->get_id();
       if (id_ptr) {
-        create_metatable<UserMetaTable>(L, false, const_cast<rgw_user*>(id_ptr));
+        create_metatable<UserMetaTable>(L, name, index, false, 
+            const_cast<rgw_user*>(id_ptr));
       } else {
         lua_pushnil(L);
       }
@@ -396,7 +383,7 @@ struct GrantMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "Referer") == 0) {
       pushstring(L, grant->get_referer());
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
@@ -409,7 +396,8 @@ struct GrantsMetaTable : public EmptyMetaTable {
   using Type = ACLGrantMap;
 
   static int IndexClosure(lua_State* L) {
-    const auto map = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto map = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -417,20 +405,13 @@ struct GrantsMetaTable : public EmptyMetaTable {
     if (it == map->end()) {
       lua_pushnil(L);
     } else {
-      create_metatable<GrantMetaTable>(L, false, &(it->second));
+      create_metatable<GrantMetaTable>(L, name, index, false, &(it->second));
     }
     return ONE_RETURNVAL;
   }
   
   static int PairsClosure(lua_State* L) {
-    auto map = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(map);
-    lua_pushlightuserdata(L, map);
-    lua_pushcclosure(L, next<Type, GrantMetaTable>, ONE_UPVAL);  // push the "next()" function
-    lua_pushnil(L);                                                 // indicate this is the first call
-    // return next, nil
-
-    return TWO_RETURNVALS;
+    return Pairs<Type, next<Type, GrantMetaTable>>(L);
   }
   
   static int LenClosure(lua_State* L) {
@@ -443,31 +424,27 @@ struct GrantsMetaTable : public EmptyMetaTable {
 };
 
 struct ACLMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "ACL";}
-  static std::string Name() {return TableName() + "Meta";}
-
   using Type = RGWAccessControlPolicy;
 
   static int IndexClosure(lua_State* L) {
-    const auto acl = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto acl = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Owner") == 0) {
-      create_metatable<OwnerMetaTable>(L, false, &(acl->get_owner()));
+      create_metatable<OwnerMetaTable>(L, name, index, false, 
+          &(acl->get_owner()));
     } else if (strcasecmp(index, "Grants") == 0) {
-      create_metatable<GrantsMetaTable>(L, false, &(acl->get_acl().get_grant_map()));
+      create_metatable<GrantsMetaTable>(L, name, index, false, &(acl->get_acl().get_grant_map()));
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct StatementsMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Statements";}
-  static std::string Name() {return TableName() + "Meta";}
-
   using Type = std::vector<rgw::IAM::Statement>;
 
   static std::string statement_to_string(const rgw::IAM::Statement& statement) {
@@ -477,7 +454,9 @@ struct StatementsMetaTable : public EmptyMetaTable {
   }
 
   static int IndexClosure(lua_State* L) {
-    const auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    std::ignore = table_name_upvalue(L);
+    const auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    ceph_assert(statements);
 
     const auto index = luaL_checkinteger(L, 2);
 
@@ -491,18 +470,12 @@ struct StatementsMetaTable : public EmptyMetaTable {
   }
   
   static int PairsClosure(lua_State* L) {
-    auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(statements);
-    lua_pushlightuserdata(L, statements);
-    lua_pushcclosure(L, stateless_iter, ONE_UPVAL); // push the stateless iterator function
-    lua_pushnil(L);                                 // indicate this is the first call
-    // return stateless_iter, nil
-
-    return TWO_RETURNVALS;
+    return Pairs<Type, stateless_iter>(L);
   }
   
   static int stateless_iter(lua_State* L) {
-    auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    std::ignore = table_name_upvalue(L);
+    auto statements = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     size_t next_it;
     if (lua_isnil(L, -1)) {
       next_it = 0;
@@ -535,13 +508,11 @@ struct StatementsMetaTable : public EmptyMetaTable {
 };
 
 struct PolicyMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Policy";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto policy = reinterpret_cast<rgw::IAM::Policy*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto policy = reinterpret_cast<rgw::IAM::Policy*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Text") == 0) {
       pushstring(L, policy->text);
@@ -553,46 +524,39 @@ struct PolicyMetaTable : public EmptyMetaTable {
         pushstring(L, policy->id.get());
       }
     } else if (strcasecmp(index, "Statements") == 0) {
-      create_metatable<StatementsMetaTable>(L, false, &(policy->statements));
+      create_metatable<StatementsMetaTable>(L, name, index, false, &(policy->statements));
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct PoliciesMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Policies";}
-  static std::string Name() {return TableName() + "Meta";}
-  
   using Type = std::vector<rgw::IAM::Policy>;
 
   static int IndexClosure(lua_State* L) {
-    const auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const auto index = luaL_checkinteger(L, 2);
 
     if (index >= (int)policies->size() || index < 0) {
       lua_pushnil(L);
     } else {
-      create_metatable<PolicyMetaTable>(L, false, &((*policies)[index]));
+      create_metatable<PolicyMetaTable>(L, name, std::to_string(index), 
+          false, &((*policies)[index]));
     }
     return ONE_RETURNVAL;
   }
   
   static int PairsClosure(lua_State* L) {
-    auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(policies);
-    lua_pushlightuserdata(L, policies);
-    lua_pushcclosure(L, stateless_iter, ONE_UPVAL); // push the stateless iterator function
-    lua_pushnil(L);                                 // indicate this is the first call
-    // return stateless_iter, nil
-
-    return TWO_RETURNVALS;
+    return Pairs<Type, stateless_iter>(L);
   }
   
   static int stateless_iter(lua_State* L) {
-    auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    auto policies = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
     size_t next_it;
     if (lua_isnil(L, -1)) {
       next_it = 0;
@@ -609,7 +573,8 @@ struct PoliciesMetaTable : public EmptyMetaTable {
       // return nil, nil
     } else {
       lua_pushinteger(L, next_it);
-      create_metatable<PolicyMetaTable>(L, false, &((*policies)[next_it]));
+      create_metatable<PolicyMetaTable>(L, name, std::to_string(next_it), 
+          false, &((*policies)[next_it]));
       // return key, value
     }
 
@@ -626,22 +591,21 @@ struct PoliciesMetaTable : public EmptyMetaTable {
 };
 
 struct HTTPMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "HTTP";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Parameters") == 0) {
-      create_metatable<StringMapMetaTable<>>(L, false, &(info->args.get_params()));
+      create_metatable<StringMapMetaTable<>>(L, name, index, false, &(info->args.get_params()));
     } else if (strcasecmp(index, "Resources") == 0) {
       // TODO: add non-const api to get resources
-      create_metatable<StringMapMetaTable<>>(L, false, 
+      create_metatable<StringMapMetaTable<>>(L, name, index, false,
           const_cast<std::map<std::string, std::string>*>(&(info->args.get_sub_resources())));
     } else if (strcasecmp(index, "Metadata") == 0) {
-      create_metatable<StringMapMetaTable<meta_map_t, StringMapWriteableNewIndex<meta_map_t>>>(L, false, &(info->x_meta_map));
+      create_metatable<StringMapMetaTable<meta_map_t, StringMapWriteableNewIndex<meta_map_t>>>(L, name, index, 
+          false, &(info->x_meta_map));
     } else if (strcasecmp(index, "Host") == 0) {
       pushstring(L, info->host);
     } else if (strcasecmp(index, "Method") == 0) {
@@ -655,75 +619,69 @@ struct HTTPMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "StorageClass") == 0) {
       pushstring(L, info->storage_class);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 
   static int NewIndexClosure(lua_State* L) {
-    auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    auto info = reinterpret_cast<req_info*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "StorageClass") == 0) {
       info->storage_class = luaL_checkstring(L, 3);
    } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
    }
     return NO_RETURNVAL;
   }
 };
 
 struct CopyFromMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "CopyFrom";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Tenant") == 0) {
       pushstring(L, s->src_tenant_name);
     } else if (strcasecmp(index, "Bucket") == 0) {
       pushstring(L, s->src_bucket_name);
     } else if (strcasecmp(index, "Object") == 0) {
-      create_metatable<ObjectMetaTable>(L, false, s->src_object);
+      create_metatable<ObjectMetaTable>(L, name, index, false, s->src_object);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct ZoneGroupMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "ZoneGroup";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
-    const char* index = luaL_checkstring(L, 2);
+    const auto index = luaL_checkstring(L, 2);
 
     if (strcasecmp(index, "Name") == 0) {
       pushstring(L, s->zonegroup_name);
     } else if (strcasecmp(index, "Endpoint") == 0) {
       pushstring(L, s->zonegroup_endpoint);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 struct RequestMetaTable : public EmptyMetaTable {
-  static std::string TableName() {return "Request";}
-  static std::string Name() {return TableName() + "Meta";}
-
-  // __index closure that expect req_state to be captured
   static int IndexClosure(lua_State* L) {
-    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    const auto op_name = reinterpret_cast<const char*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    const auto name = table_name_upvalue(L);
+    const auto s = reinterpret_cast<req_state*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    const auto op_name = reinterpret_cast<const char*>(lua_touserdata(L, lua_upvalueindex(THIRD_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -734,9 +692,9 @@ struct RequestMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "ContentLength") == 0) {
       lua_pushinteger(L, s->content_length);
     } else if (strcasecmp(index, "GenericAttributes") == 0) {
-      create_metatable<StringMapMetaTable<>>(L, false, &(s->generic_attrs));
+      create_metatable<StringMapMetaTable<>>(L, name, index, false, &(s->generic_attrs));
     } else if (strcasecmp(index, "Response") == 0) {
-      create_metatable<ResponseMetaTable>(L, false, &(s->err));
+      create_metatable<ResponseMetaTable>(L, name, index, false, &(s->err));
     } else if (strcasecmp(index, "SwiftAccountName") == 0) {
       if (s->dialect == "swift") {
         pushstring(L, s->account_name);
@@ -744,40 +702,40 @@ struct RequestMetaTable : public EmptyMetaTable {
         lua_pushnil(L);
       }
     } else if (strcasecmp(index, "Bucket") == 0) {
-      create_metatable<BucketMetaTable>(L, false, s);
+      create_metatable<BucketMetaTable>(L, name, index, false, s);
     } else if (strcasecmp(index, "Object") == 0) {
-      create_metatable<ObjectMetaTable>(L, false, s->object);
+      create_metatable<ObjectMetaTable>(L, name, index, false, s->object);
     } else if (strcasecmp(index, "CopyFrom") == 0) {
       if (s->op_type == RGW_OP_COPY_OBJ) {
-        create_metatable<CopyFromMetaTable>(L, false, s);
+        create_metatable<CopyFromMetaTable>(L, name, index, false, s);
       } else {
         lua_pushnil(L);
       }
     } else if (strcasecmp(index, "ObjectOwner") == 0) {
-      create_metatable<OwnerMetaTable>(L, false, &(s->owner));
+      create_metatable<OwnerMetaTable>(L, name, index, false, &(s->owner));
     } else if (strcasecmp(index, "ZoneGroup") == 0) {
-      create_metatable<ZoneGroupMetaTable>(L, false, s);
+      create_metatable<ZoneGroupMetaTable>(L, name, index, false, s);
     } else if (strcasecmp(index, "UserACL") == 0) {
-      create_metatable<ACLMetaTable>(L, false, s->user_acl);
+      create_metatable<ACLMetaTable>(L, name, index, false, s->user_acl);
     } else if (strcasecmp(index, "BucketACL") == 0) {
-      create_metatable<ACLMetaTable>(L, false, s->bucket_acl);
+      create_metatable<ACLMetaTable>(L, name, index, false, s->bucket_acl);
     } else if (strcasecmp(index, "ObjectACL") == 0) {
-      create_metatable<ACLMetaTable>(L, false, s->object_acl);
+      create_metatable<ACLMetaTable>(L, name, index, false, s->object_acl);
     } else if (strcasecmp(index, "Environment") == 0) {
-        create_metatable<StringMapMetaTable<rgw::IAM::Environment>>(L, false, &(s->env));
+        create_metatable<StringMapMetaTable<rgw::IAM::Environment>>(L, name, index, false, &(s->env));
     } else if (strcasecmp(index, "Policy") == 0) {
       // TODO: create a wrapper to std::optional
       if (!s->iam_policy) {
         lua_pushnil(L);
       } else {
-        create_metatable<PolicyMetaTable>(L, false, s->iam_policy.get_ptr());
+        create_metatable<PolicyMetaTable>(L, name, index, false, s->iam_policy.get_ptr());
       }
     } else if (strcasecmp(index, "UserPolicies") == 0) {
-        create_metatable<PoliciesMetaTable>(L, false, &(s->iam_user_policies));
+        create_metatable<PoliciesMetaTable>(L, name, index, false, &(s->iam_user_policies));
     } else if (strcasecmp(index, "RGWId") == 0) {
       pushstring(L, s->host_id);
     } else if (strcasecmp(index, "HTTP") == 0) {
-        create_metatable<HTTPMetaTable>(L, false, &(s->info));
+        create_metatable<HTTPMetaTable>(L, name, index, false, &(s->info));
     } else if (strcasecmp(index, "Time") == 0) {
       pushtime(L, s->time);
     } else if (strcasecmp(index, "Dialect") == 0) {
@@ -787,26 +745,28 @@ struct RequestMetaTable : public EmptyMetaTable {
     } else if (strcasecmp(index, "TransactionId") == 0) {
       pushstring(L, s->trans_id);
     } else if (strcasecmp(index, "Tags") == 0) {
-      create_metatable<StringMapMetaTable<RGWObjTags::tag_map_t>>(L, false, &(s->tagset.get_tags()));
+      create_metatable<StringMapMetaTable<RGWObjTags::tag_map_t>>(L, name, index, false, &(s->tagset.get_tags()));
     } else if (strcasecmp(index, "User") == 0) {
       if (!s->user) {
         lua_pushnil(L);
       } else {
-        create_metatable<UserMetaTable>(L, false, const_cast<rgw_user*>(&(s->user->get_id())));
+        create_metatable<UserMetaTable>(L, name, index, false,
+            const_cast<rgw_user*>(&(s->user->get_id())));
       }
     } else if (strcasecmp(index, "Trace") == 0) {
-        create_metatable<TraceMetaTable>(L, false, s);
+        create_metatable<TraceMetaTable>(L, name, index, false, s);
     } else {
-      return error_unknown_field(L, index, TableName());
+      return error_unknown_field(L, index, name);
     }
     return ONE_RETURNVAL;
   }
 };
 
 void create_top_metatable(lua_State* L, req_state* s, const char* op_name) {
-  create_metatable<RequestMetaTable>(L, true, s, const_cast<char*>(op_name));
-  lua_getglobal(L, RequestMetaTable::TableName().c_str());
-  ceph_assert(lua_istable(L, -1));
+  static const char* request_table_name = "Request";
+  create_metatable<RequestMetaTable>(L, "", request_table_name, true, s, const_cast<char*>(op_name));
+  const auto type = lua_getglobal(L, request_table_name);
+  ceph_assert(type == LUA_TTABLE);
 }
 
 int execute(
@@ -831,11 +791,8 @@ int execute(
     set_package_path(L, s->penv.lua.luarocks_path);
 
     create_debug_action(L, s->cct);  
-    
-    create_metatable<RequestMetaTable>(L, true, s, const_cast<char*>(op_name));
-
-    lua_getglobal(L, RequestMetaTable::TableName().c_str());
-    ceph_assert(lua_istable(L, -1));
+  
+    create_top_metatable(L, s, const_cast<char*>(op_name));  
 
     // add the ops log action
     pushstring(L, RequestLogAction);
@@ -845,11 +802,9 @@ int execute(
     lua_pushlightuserdata(L, op);
     lua_pushcclosure(L, RequestLog, FOUR_UPVALS);
     lua_rawset(L, -3);
-    
+  
     if (s->penv.lua.background) {
       s->penv.lua.background->create_background_metatable(L);
-      lua_getglobal(L, rgw::lua::RGWTable::TableName().c_str());
-      ceph_assert(lua_istable(L, -1));
     }
 
     // execute the lua script
index e4dcde1186847ff2eaaeb5a941cc44777d59d3f8..0e8dfb2bf6ba923e2654cc69d93b860f79c0ffd9 100644 (file)
@@ -97,45 +97,57 @@ constexpr auto ONE_RETURNVAL    = 1;
 constexpr auto TWO_RETURNVALS   = 2;
 constexpr auto THREE_RETURNVALS = 3;
 constexpr auto FOUR_RETURNVALS  = 4;
-// utility functions to create a metatable
+
+// create_metatable() is a utility functions to create a metatable
 // and tie it to an unnamed table
 //
-// add an __index method to it, to allow reading values
+// it add an __index method to it, to allow reading values
 // if "readonly" parameter is set to "false", it will also add
 // a __newindex method to it, to allow writing values
 // if the "toplevel" parameter is set to "true", it will name the
 // table as well as the metatable, this would allow direct access from
 // the lua script.
+// the name of the metatable will be a fully qualified name, based on the names
+// of the metatables that contain it, separated by dots.
+// it also adds a __len and  __pairs methods for iterable entities.
 //
 // The MetaTable is expected to be a class with the following members:
-// Name (static function returning the unique name of the metatable)
-// TableName (static function returning the unique name of the table - needed only for "toplevel" tables)
 // Type (typename) - the type of the "upvalue" (the type that the meta table represent)
 // IndexClosure (static function return "int" and accept "lua_State*") 
 // NewIndexClosure (static function return "int" and accept "lua_State*") 
+// PairsClosure (static function return "int" and accept "lua_State*") 
+// LenClosure (static function return "int" and accept "lua_State*") 
+//
 // e.g.
 // struct MyStructMetaTable {
-//   static std::string TableName() {
-//     return "MyStruct";
-//   }
 //
 //   using Type = MyStruct;
 //
 //   static int IndexClosure(lua_State* L) {
-//     const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+//     const auto name = tatable_name_upvalue(L);
+//     const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 //     ...
 //   }
 
 //   static int NewIndexClosure(lua_State* L) {
+//     const auto name = table_name_upvalue(L);
 //     auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
 //     ...
 //   }
 // };
 //
+// If the structs inherits from EmptyMetatable then the only function that needs 
+// to be implemented is IndexClosure. The other functions are implemented in 
+// the base class stating that the table is "readonly" and "not-iterable".
 
 template<typename MetaTable, typename... Upvalues>
-void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues)
+void create_metatable(lua_State* L, std::string_view parent_name, std::string_view field_name,
+    bool toplevel, Upvalues... upvalues)
 {
+  const std::string qualified_name(fmt::format("{}{}{}", 
+        parent_name,
+        (parent_name.empty() ? "" : "."),
+        field_name));
   constexpr auto upvals_size = sizeof...(upvalues);
   const std::array<void*, upvals_size> upvalue_arr = {upvalues...};
   // create table
@@ -144,34 +156,44 @@ void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues)
     // duplicate the table to make sure it remain in the stack
     lua_pushvalue(L, -1);
     // give table a name (in cae of "toplevel")
-    lua_setglobal(L, MetaTable::TableName().c_str());
+    lua_setglobal(L, qualified_name.c_str());
+  }
+
+  // create/reuse metatable
+  const auto metatable_is_new = luaL_newmetatable(L, qualified_name.c_str());
+  if (!metatable_is_new) {
+    // tie metatable to table
+    lua_setmetatable(L, -2);
+    return;
   }
-  // create metatable
-  [[maybe_unused]] const auto rc = luaL_newmetatable(L, MetaTable::Name().c_str());
+
   const auto table_stack_pos = lua_gettop(L);
 
   // add "index" closure to metatable
   lua_pushliteral(L, "__index");
+  pushstring(L, qualified_name);
   for (const auto upvalue : upvalue_arr) {
     lua_pushlightuserdata(L, upvalue);
   }
-  lua_pushcclosure(L, MetaTable::IndexClosure, upvals_size);
+  lua_pushcclosure(L, MetaTable::IndexClosure, upvals_size+1);
   lua_rawset(L, table_stack_pos);
 
   // add "newindex" closure to metatable
   lua_pushliteral(L, "__newindex");
+  pushstring(L, qualified_name);
   for (const auto upvalue : upvalue_arr) {
     lua_pushlightuserdata(L, upvalue);
   }
-  lua_pushcclosure(L, MetaTable::NewIndexClosure, upvals_size);
+  lua_pushcclosure(L, MetaTable::NewIndexClosure, upvals_size+1);
   lua_rawset(L, table_stack_pos);
 
   // add "pairs" closure to metatable
   lua_pushliteral(L, "__pairs");
+  pushstring(L, qualified_name);
   for (const auto upvalue : upvalue_arr) {
     lua_pushlightuserdata(L, upvalue);
   }
-  lua_pushcclosure(L, MetaTable::PairsClosure, upvals_size);
+  lua_pushcclosure(L, MetaTable::PairsClosure, upvals_size+1);
   lua_rawset(L, table_stack_pos);
 
   // add "len" closure to metatable
@@ -179,24 +201,24 @@ void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues)
   for (const auto upvalue : upvalue_arr) {
     lua_pushlightuserdata(L, upvalue);
   }
+  // "name" is not needed at the "len" closure
   lua_pushcclosure(L, MetaTable::LenClosure, upvals_size);
   lua_rawset(L, table_stack_pos);
 
-  // tie metatable and table
-  ceph_assert(lua_gettop(L) == table_stack_pos);
+  // tie metatable to table
   lua_setmetatable(L, -2);
 }
 
 template<typename MetaTable>
-void create_metatable(lua_State* L, bool toplevel, std::unique_ptr<typename MetaTable::Type>& ptr)
+void create_metatable(lua_State* L, const std::string_view parent_name, const std::string_view field_name,
+    bool toplevel, std::unique_ptr<typename MetaTable::Type>& ptr)
 {
   if (ptr) {
-    create_metatable<MetaTable>(L, toplevel, reinterpret_cast<void*>(ptr.get()));
+    create_metatable<MetaTable>(L, parent_name, field_name, toplevel, reinterpret_cast<void*>(ptr.get()));
   } else {
     lua_pushnil(L);
   }
 }
-
 // following struct may be used as a base class for other MetaTable classes
 // note, however, this is not mandatory to use it as a base
 struct EmptyMetaTable {
@@ -249,17 +271,31 @@ void open_standard_libs(lua_State* L);
 
 typedef int MetaTableClosure(lua_State* L);
 
+// get iterator name from table name
+inline std::string get_iterator_name(const std::string_view name) {
+  return fmt::format("{}.Iterator", name);
+}
+
+// the fully qualified name of the table is expected to be the 1st upvalue of the closure
+inline const char* table_name_upvalue(lua_State* L) {
+  const auto name = reinterpret_cast<const char*>(lua_tostring(L, lua_upvalueindex(FIRST_UPVAL)));
+  ceph_assert(name);
+  return name;
+}
+
 // copy the input iterator into a new iterator with memory allocated as userdata
 // - allow for string conversion of the iterator (into its key)
 // - storing the iterator in the metadata table to be used for iterator invalidation handling
 // - since we have only one iterator per map/table we don't allow for nested loops or another iterationn
 // after breaking out of an iteration
 template<typename MapType>
-typename MapType::iterator* create_iterator_metadata(lua_State* L, const typename MapType::iterator& start_it, const typename MapType::iterator& end_it) {
+typename MapType::iterator* create_iterator_metadata(lua_State* L, const std::string_view name, 
+    const typename MapType::iterator& start_it, const typename MapType::iterator& end_it) {
   using Iterator = typename MapType::iterator;
+  const std::string qualified_name = get_iterator_name(name);
   // create metatable for userdata
   // metatable is created before the userdata to save on allocation if the metatable already exists
-  const auto metatable_is_new = luaL_newmetatable(L, typeid(typename MapType::key_type).name());
+  const auto metatable_is_new = luaL_newmetatable(L, qualified_name.c_str());
   const auto metatable_pos = lua_gettop(L);
   int userdata_pos;
   Iterator* new_it = nullptr;
@@ -271,8 +307,7 @@ typename MapType::iterator* create_iterator_metadata(lua_State* L, const typenam
     auto old_it = reinterpret_cast<typename MapType::iterator*>(lua_touserdata(L, -1));
     // verify we are not mid-iteration
     if (*old_it != end_it) {
-      luaL_error(L, "Trying to iterate '%s' before previous iteration finished", 
-          typeid(typename MapType::key_type).name());
+      luaL_error(L, "Trying to iterate '%s' before previous iteration finished", name.data());
       return nullptr;
     } 
     // we use the same memory buffer
@@ -321,10 +356,12 @@ typename MapType::iterator* create_iterator_metadata(lua_State* L, const typenam
   return new_it;
 }
 
+// when a value of a table is erased while the table is being iterated
+// this function allows updating the stored iterator with a new valid one
 template<typename MapType>
-void update_erased_iterator(lua_State* L, const typename MapType::iterator& old_it, const typename MapType::iterator& new_it) {
+void update_erased_iterator(lua_State* L, const std::string_view name, const typename MapType::iterator& old_it, const typename MapType::iterator& new_it) {
   // a metatable exists for the iterator
-  if (luaL_getmetatable(L, typeid(typename MapType::key_type).name()) != LUA_TNIL) {
+  if (luaL_getmetatable(L, get_iterator_name(name).c_str()) != LUA_TNIL) {
     const auto metatable_pos = lua_gettop(L);
     lua_pushliteral(L, "__iterator");
     if (lua_rawget(L, metatable_pos) != LUA_TNIL) {
@@ -348,7 +385,8 @@ template<typename MapType=std::map<std::string, std::string>>
 int StringMapWriteableNewIndex(lua_State* L) {
   static_assert(std::is_constructible<typename MapType::key_type, const char*>());
   static_assert(std::is_constructible<typename MapType::mapped_type, const char*>());
-  const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+  const auto name = table_name_upvalue(L);
+  const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
   ceph_assert(map);
 
   const char* index = luaL_checkstring(L, 2);
@@ -367,7 +405,7 @@ int StringMapWriteableNewIndex(lua_State* L) {
     // erase the element. since in lua: "t[index] = nil" is removing the entry at "t[index]"
     if (const auto it = map->find(index); it != map->end()) {
       // index was found
-      update_erased_iterator<MapType>(L, it, map->erase(it));
+      update_erased_iterator<MapType>(L, name, it, map->erase(it));
     }
   }
 
@@ -382,7 +420,8 @@ int StringMapWriteableNewIndex(lua_State* L) {
 template<typename MapType, typename ValueMetaType=void>
 int next(lua_State* L) {
   using Iterator = typename MapType::iterator;
-  auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
+  const auto name = table_name_upvalue(L);
+  auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
   ceph_assert(map);
   Iterator* next_it = nullptr;
 
@@ -390,12 +429,12 @@ int next(lua_State* L) {
     // pop the 2 nils
     lua_pop(L, 2);
     // create userdata
-    next_it = create_iterator_metadata<MapType>(L, map->begin(), map->end());
+    next_it = create_iterator_metadata<MapType>(L, name, map->begin(), map->end());
   } else {
     next_it = reinterpret_cast<Iterator*>(lua_touserdata(L, 2));
-    ceph_assert(next_it);
     *next_it = std::next(*next_it);
   }
+  ceph_assert(next_it);
 
   if (*next_it == map->end()) {
     // index of the last element was provided
@@ -417,22 +456,35 @@ int next(lua_State* L) {
     std::visit([L](auto&& value) { pushvalue(L, value); }, value);
   } else {
     // as a metatable
-    create_metatable<ValueMetaType>(L, false, &(value));
+    create_metatable<ValueMetaType>(L, name, (*next_it)->first,
+        false, &(value));
   }
   // return key, value
   return TWO_RETURNVALS;
 }
 
+// a generic "Pairs" utility function that could be used as the metatable's "PairsClosure" function
+template<typename MapType, MetaTableClosure Next=next<MapType>>
+int Pairs(lua_State* L) {
+    const auto name = table_name_upvalue(L);
+    auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
+    ceph_assert(map);
+    pushstring(L, name);
+    lua_pushlightuserdata(L, map);
+    lua_pushcclosure(L, Next, TWO_UPVALS); // push the "next()" function
+    lua_pushnil(L);                        // indicate this is the first call
+    // return next, nil
+
+    return TWO_RETURNVALS;
+}
+
+
 template<typename MapType=std::map<std::string, std::string>,
   MetaTableClosure NewIndex=EmptyMetaTable::NewIndexClosure>
 struct StringMapMetaTable : public EmptyMetaTable {
-
-  static std::string TableName() {return "StringMap";}
-  static std::string Name() {return TableName() + "Meta";}
-
   static int IndexClosure(lua_State* L) {
-    const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(map);
+    std::ignore = table_name_upvalue(L);
+    const auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(SECOND_UPVAL)));
 
     const char* index = luaL_checkstring(L, 2);
 
@@ -450,14 +502,7 @@ struct StringMapMetaTable : public EmptyMetaTable {
   }
 
   static int PairsClosure(lua_State* L) {
-    auto map = reinterpret_cast<MapType*>(lua_touserdata(L, lua_upvalueindex(FIRST_UPVAL)));
-    ceph_assert(map);
-    lua_pushlightuserdata(L, map);
-    lua_pushcclosure(L, next<MapType>, ONE_UPVAL); // push the "next()" function
-    lua_pushnil(L);                                // indicate this is the first call
-    // return next, nil
-
-    return TWO_RETURNVALS;
+    return Pairs<MapType>(L);
   }
   
   static int LenClosure(lua_State* L) {
index ca0c9ae1d3e25a188175d7eda6ee7684015f4b86..2df8047a4aa6fd2034989e407ec94d63045461c1 100644 (file)
@@ -516,24 +516,25 @@ TEST(TestRGWLua, WriteMetadata)
 TEST(TestRGWLua, MetadataIterateWrite)
 {
   const std::string script = R"(
+    assert(#Request.HTTP.Metadata == 7)
     counter = 0
     for k,v in pairs(Request.HTTP.Metadata) do
       counter = counter + 1
-      print(k,v)
       if tostring(k) == "c" then
         Request.HTTP.Metadata["c"] = nil
-        print("'c' is deleted and 'd' is skipped")
+        print("'c' is deleted and iterator moves")
+        assert(tostring(k) ~= "c")
       end
     end
     assert(counter == 6)
     counter = 0
     for k,v in pairs(Request.HTTP.Metadata) do
       counter = counter + 1
-      print(k,v)
       if tostring(k) == "d" then
         Request.HTTP.Metadata["e"] = nil
         print("'e' is deleted")
       end
+      assert(tostring(k) ~= "e")
     end
     assert(counter == 5)
   )";
@@ -565,12 +566,14 @@ TEST(TestRGWLua, MetadataIterator)
   s.info.x_meta_map["g"] = "7";
   
   std::string script = R"(
-    print("nested loop")
+    -- nested loop
     counter = 0
     for k1,v1 in pairs(Request.HTTP.Metadata) do
-      print(tostring(k1)..","..v1.." outer loop "..tostring(counter))
+      assert(k1)
+      assert(v2)
       for k2,v2 in pairs(Request.HTTP.Metadata) do
-        print(k2,v2)
+        print("we should not reach this line")
+        print(k2, v2)
       end
       counter = counter + 1
     end
@@ -580,18 +583,20 @@ TEST(TestRGWLua, MetadataIterator)
   ASSERT_NE(rc, 0);
   
   script = R"(
-    print("break loop")
+    -- break loop
     counter = 0
     for k,v in pairs(Request.HTTP.Metadata) do
       counter = counter + 1
-      print(k,v)
+      assert(k)
+      assert(v)
       if counter == 3 then
         break
       end
       counter = counter + 1
     end
-    print("full loop")
+    -- full loop
     for k,v in pairs(Request.HTTP.Metadata) do
+        print("we should not reach this line")
       print(k,v)
     end
   )";
@@ -600,16 +605,18 @@ TEST(TestRGWLua, MetadataIterator)
   ASSERT_NE(rc, 0);
   
   script = R"(
-    print("2 loops")
+    -- 2 loops
     counter = 0
     for k,v in pairs(Request.HTTP.Metadata) do
-      print(k,v)
-      counter = counter + 1
+     assert(k)
+     assert(v)
+     counter = counter + 1
     end
     assert(counter == #Request.HTTP.Metadata)
     counter = 0
     for k,v in pairs(Request.HTTP.Metadata) do
-      print(k,v)
+      assert(k)
+      assert(v)
       counter = counter + 1
     end
     assert(counter == #Request.HTTP.Metadata)
@@ -1239,9 +1246,10 @@ TEST(TestRGWLuaBackground, TableIterateWrite)
     counter = 0 
     for k, v in pairs(RGW) do
       counter = counter + 1
-      print(k, v)
       if tostring(k) == "c" then
         RGW["c"] = nil
+        print("'c' is deleted and iterator moves")
+        assert(tostring(k) ~= "c")
       end
     end
     assert(counter == 4)
@@ -1540,3 +1548,56 @@ TEST(TestRGWLua, MemoryLimit)
   ASSERT_NE(rc, 0);
 }
 
+TEST(TestRGWLua, DifferentContextUser)
+{
+  const std::string script = R"(
+    assert(Request.User.Id == "user1")
+    assert(Request.User.Tenant == "tenant1")
+    assert(Request.Bucket.User.Id == "user2")
+    assert(Request.Bucket.User.Tenant == "tenant2")
+    local u1 = Request.User
+    local u2 = Request.Bucket.User
+    assert(u1.Id == "user1")
+    assert(u2.Id == "user2")
+    assert(u1.Tenant == "tenant1")
+    assert(u2.Tenant == "tenant2")
+  )";
+
+  DEFINE_REQ_STATE;
+
+  s.user.reset(new sal::RadosUser(nullptr, rgw_user("tenant1", "user1")));
+  rgw_bucket b;
+  b.name = "bucket1";
+  s.bucket.reset(new sal::RadosBucket(nullptr, b));
+  s.bucket->set_owner(new sal::RadosUser(nullptr, rgw_user("tenant2", "user2")));
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}
+
+TEST(TestRGWLua, NestedLoop)
+{
+  const std::string script = R"(
+  for k1, v1 in pairs(Request.Environment) do
+    assert(k1)
+    assert(v1)
+    for k2, v2 in pairs(Request.HTTP.Metadata) do
+      assert(k2)
+      assert(v2)
+    end
+  end
+  )";
+
+  DEFINE_REQ_STATE;
+  s.env.emplace("1", "a");
+  s.env.emplace("2", "b");
+  s.env.emplace("3", "c");
+  s.info.x_meta_map["11"] = "aa";
+  s.info.x_meta_map["22"] = "bb";
+  s.info.x_meta_map["33"] = "cc";
+  s.info.x_meta_map["44"] = "dd";
+
+  const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, nullptr, script);
+  ASSERT_EQ(rc, 0);
+}
+