Project

General

Profile

Actions

Support #13147

open

pass a unique_ptr parameter with custom deleter to Session's setConnection method

Added by Giovanni Ferranti about 2 months ago. Updated about 2 months ago.

Status:
Feedback
Priority:
Normal
Target version:
-
Start date:
10/23/2024
Due date:
% Done:

0%

Estimated time:

Description

Hi,

Is it possible in your opinion to pass a unique_ptr parameter with custom deleter to dbo Session's setConnection method? If so, how do I properly convert the parameter before passing it to the function? I want to call my custom deleter instead of the default when I go out of scope.

Thank you in advance,

Giovanni


Files

sqldeleter.patch (20 KB) sqldeleter.patch Matthias Van Ceulebroeck, 10/29/2024 02:42 PM
Actions #1

Updated by Matthias Van Ceulebroeck about 2 months ago

  • Status changed from New to Feedback
  • Assignee set to Giovanni Ferranti

Hello Giovanni,

it is not possible to add a custom Deleter to the unique_ptr, since that would be regarded as a different type to the default deleter. When calling setConnection, the conversion from your custom Deleter to the std::default_delete<Wt::Dbo::SqlConnection>> would fail.

Now, what IS possible, is to create a wrapper around your connection object. A small example would look like this:

class ConnectionWrapper : public Wt::Dbo::backend::Sqlite3                                                                                                                                                                                                   
{
public:
  ConnectionWrapper(const std::string& location)
    : Wt::Dbo::backend::Sqlite3(location)
  {
  }

  ~ConnectionWrapper()
  {
    Wt::log("==========") <<  "DO DESTRUCTION";
  }
};

It can be created like any other backend, and then set as the session's connection:

ConnectionWrapper *sqlite3 = new ConnectionWrapper(appRoot() + "database.db");
auto connection = std::unique_ptr<Wt::Dbo::SqlConnection>(sqlite3);
session_.setConnection(std::move(connection));

Note that I took Sqlite3 as the example here, but it ought to work for any backend.

Actions #2

Updated by Giovanni Ferranti about 2 months ago

Hi Matthias, thank you very much for your reply. Unfortunately I need to avoid the connection deletion. I managed to independently recompile the wt library and in particular the wtdbo project. Do you think I can replace the declaration

std::unique_ptr<SqlConnection, nop> connection_;

with

std::unique_ptr<SqlConnection> connection_;

where nop is defined as:

#ifndef NOP_HPP
#define NOP_HPP

class SqlConnection;

struct nop
{
void operator() (SqlConnection* c) const noexcept { }
};

#endif // NOP_HPP

What is the most transparent way to do it?

I tried but if I include nop.h in Transaction.h it gives me error C3646: 'limitQueryMethod_': unknown override specifier in Session.h

Thank you,

Giovanni

Actions #3

Updated by Matthias Van Ceulebroeck about 2 months ago

Hello Giovanni,

that is because you would need to change all occurrences of the SqlConnection unique_ptr instances. I have created a quick patch to allow you to do this:

Actions #4

Updated by Giovanni Ferranti about 2 months ago

Hello Matthias.

sorry for the delay in replying but I have been abroad. Thank you very much, I have already successfully recompiled the wtdbo library using a custom deleter that does nothing. It works but I will have to manage other things to make everything work correctly.
It is a bit challenging because I am working on a scripting language based on Chaiscript engine which, among other things, incorporates most of the wt, including dbo. wt part works pretty well (I succesfully recreated the whole simplechat example) but I've some troubles with dbo, maybe because of external dll generation.

Basically I can do something like this:

// this script will generate a dynamic library called DbModule.dll with all relative dbo methods exposed by script engine
import("cg")
var dbObj = DbObject("TabMaterial")
dbObj.SetTypes(["string", "string", "string", "string", "string", "string"])
dbObj.SetNames(["description", "net_weight_V", "uom", "old_material_code_s", "type_S", "SETUP_GROUP"])
dbObj.SetDefaults(["", "", "", "", "", ""])
dbObj.SetNaturalPrimaryKey("part_number", "string", "part_number", 64)
dbObj.DisableVersionField(true)
cg.AddDatabaseObject(dbObj)
cg.Commit()
cg.Compile() // compiles the .dll

and this:

load_module("DbModule")

var web = WebServer("./restApi.wt", 8081)

var resource = WResource(fun(req, res) {
    if(req.Method != "POST") {
        res.Out << "Error! Only Post Method is allowed!"
        return
    }

    var conn = SQLServer(CONNECTION_STRING)
    var session = Session()
    session.MapClassTabMaterial("TAB_MATERIAL_SOURCE")
    session.SetConnection(conn)
        session.CreateTables()

    var body = ""
    var len = req.ContentLength
    var read = 0
    var chunck = ""

    while(read < len) {
        req.In >> chunck
        read += chunck.size
        body += chunck
    }

    var part_number = body.from_json["materialCode"]
    var description = body.from_json["materialDescription"]
    var netWeight = body.from_json["netWeight"]["value"]
    var uom = body.from_json["netWeight"]["unit"]
    var oldMaterialCode = body.from_json["oldMaterialCode"]
    var materialType = body.from_json["materialType"]
    var t = Transaction(session)
    var tabMaterial = TabMaterial()

    try {
        tabMaterial.part_number = part_number
        tabMaterial.description = description
        tabMaterial.net_weight_V = to_string(netWeight)
        tabMaterial.uom = uom
        tabMaterial.old_material_code_s = oldMaterialCode
        tabMaterial.type_S = materialType
        tabMaterial.SETUP_GROUP = ""
        session.AddTabMaterial(tabMaterial)
    }
    catch(e) {
        t.Rollback
        res.Out << "Db Error!" + e.what()
        return
    }
    t.Commit
    res.Out << "Success!"   
})

var f = fun(app) {
    print("Web Api Server started!")
}
web.Run(f)
web.AddResource(resource, "/api")

it works but I have some collateral effects because of MSSQLServer and Session objects declared inside a chaiscript function. if declared outside it works fine. I guess it could be a Chaiscript bug but it is actually too complex for me trying to fix the engine source code. So I will try to adjust wtdbo for my needs :) :)

Thanks again for your availability and kindness

Actions #5

Updated by Matthias Van Ceulebroeck about 2 months ago

Hello Giovanni,

I suspect that the server (and its connection) should stay alive. All data is stored in a session, and if that object is destructed, so would its contents. Used as an API, though, the connection is likely fine to be destructed. But I'd be careful with session up too many connections.

It's not a use-case I have seen before. But generally, since it's an API, you way want to take a look at a connection pool? That can ensure their is not much overhead from setting up new connections each time. A suggestion, and I am also not sure if it fits with your needs.

Best of luck, and let me know should there be anything else!

Actions

Also available in: Atom PDF