Discussion - ORM Techniques

closed account (3hM2Nwbp)
I've suddenly found myself needing to dynamically cache a database, so I ran to Google and searched...resulting in the too-good-to-be-true solution of ODB - a free cross platform ORM solution. Naturally I was excited by this, because it was another chunk of code that I didn't have to write...or was it?

Reading onward into the product, I came across this in the license:
Parasitic Licensing Entity wrote:

By linking with the ODB runtime libraries (directly or indirectly,
statically or dynamically, at compile time or runtime), your
application is subject to the terms of the GPL version 2 which
requires that you release the source code of your application if
and when you distribute it.


which is absolutely unacceptable in this case, but it was nice that they actually came out and said it rather than wrapping it up in a 100 page veil of legalese.

That got me thinking - how robust does this code need to be? The answer: Not very robust - which lead me to the following write up. The main thing that I don't like about it is the if/else chain structure. I might redo it with a function map, but I'd like some fresh ideas / eyes.

1
2
/// Mapping database field names to their values.
std::map<std::string, Attribute>



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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/// Attribute.hpp
/// Quick 'N Dirty

#ifndef ATTRIBUTE_HPP_INCLUDED
#define ATTRIBUTE_HPP_INCLUDED

#include <algorithm>
#include <typeinfo>

// Concept 'stolen' from boost::any but dumbed down (may not cover the O/S in your cell phone...)

class Attribute
{

	struct placeholder
	{
            virtual const std::type_info & getType() const = 0;
            virtual placeholder * clone() const = 0;
	};

	template<typename Data_Type>
	struct holder : public placeholder
	{
		friend Attribute;
		public:
			holder(const Data_Type& data) : data(data) { }

			virtual const std::type_info& getType(void) const
			{
				return typeid(Data_Type);
			}

			virtual placeholder* clone(void) const
			{
				return new holder(data);
			}
		private:

			Data_Type data;
	};

	public:

		Attribute(void) : data(nullptr)
		{

		}

		template<typename Data_Type>
		Attribute(const Data_Type& data) : data(new holder<Data_Type>(data))
		{

		}

		~Attribute(void)
		{
			delete data;
		}

		template<typename Data_Type>
		const Data_Type& getData(void) const
		{
			if(typeid(Data_Type) != data->getType())
				throw std::logic_error(std::string("A bad cast was attempted!));
/* (") + std::string(data->getType().name())
 + std::string(" to ") + std::string(typeid(Data_Type).name())
 + std::string(")"));*/
			return dynamic_cast<holder<Data_Type>*>(data)->data;
		}

		const std::type_info & type() const
		{
			return data ? data->getType() : typeid(void);
		}

		bool isEmpty(void) const
		{
			return data == nullptr;
		}

		Attribute& swap(Attribute& rhs)
		{
			std::swap(data, rhs.data);
			return *this;
		}

		template<typename Data_Type>
		Attribute& operator=(const Data_Type& data)
		{
			Attribute(data).swap(*this);
			return *this;
		}
		
		Attribute& operator=(Attribute rhs)
		{
			rhs.swap(*this);
			return *this;
		}

	private:
		
		placeholder* data;


};

#endif


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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/// AttributeLoader.hpp
/// Quick 'N Dirty
#ifndef ATTRIBUTE_LOADER_HPP_INCLUDED
#define ATTRIBUTE_LOADER_HPP_INCLUDED

#include <map>
#include <stdexcept>
#include <string>

std::map<std::string, Attribute> loadAttributes(const ResultSet& rs)
{
	std::map<std::string, Attribute> attributes;
	if(rs)
	{
		sql::ResultSetMetaData* meta = rs->getMetaData();
		for(unsigned int i = 1; i < meta->getColumnCount(); ++i)
		{
			std::string alias = meta->getColumnName(i);
			switch(meta->getColumnType(i))
			{
				case sql::DataType::TINYINT: // 8b
					if(meta->isSigned(i))
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<signed char>(rs->getInt(alias)))));
					}
					else
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<unsigned char>(rs->getUInt(alias)))));
					}
					break;
				case sql::DataType::SMALLINT: // 16b
					if(meta->isSigned(i))
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<signed short>(rs->getInt(alias)))));
					}
					else
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<unsigned short>(rs->getUInt(alias)))));
					}
					break;
				case sql::DataType::MEDIUMINT: // 24b
				case sql::DataType::INTEGER:   // 32b
					if(meta->isSigned(i))
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<signed int>(rs->getInt(alias)))));
					}
					else
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<unsigned int>(rs->getUInt(alias)))));
					}
					break;
				case sql::DataType::BIGINT:    // 64b
					if(meta->isSigned(i))
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<signed long long>(rs->getInt64(alias)))));
					}
					else
					{
						attributes.insert(std::make_pair(alias, Attribute(static_cast<unsigned long long>(rs->getUInt64(alias)))));
					}
					break;
				case sql::DataType::REAL:      // 32b
					attributes.insert(std::make_pair(alias, Attribute(static_cast<float>(rs->getDouble(alias)))));
					break;
				case sql::DataType::DOUBLE:    // 64b
					attributes.insert(std::make_pair(alias, Attribute(static_cast<long double>(rs->getDouble(alias)))));
					break;
				case sql::DataType::DECIMAL:
				case sql::DataType::NUMERIC:  // Unspecified
					attributes.insert(std::make_pair(alias, Attribute(static_cast<double>(rs->getDouble(alias)))));
					break;
				case sql::DataType::VARCHAR:
					attributes.insert(std::make_pair(alias, Attribute(static_cast<std::string>(rs->getString(alias).asStdString()))));
					break;
			}
		}
	}
	return attributes;
}

		void saveAttributes(unsigned long long user, const std::map<std::string, Attribute>& attributes)
		{
			sql::SQLString query = "UPDATE `test_table` SET `";
			for(std::map<std::string, Attribute>::const_iterator iter = attributes.cbegin(); iter != attributes.cend(); ++iter)
			{
				const std::type_info& type = iter->second.type();
				sql::SQLString value = "";
				if(type == typeid(bool))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<bool>());
				}
				else if(type == typeid(unsigned char))
				{
					value = boost::lexical_cast<std::string>(static_cast<unsigned int>(iter->second.getData<unsigned char>()));
				}
				else if(type == typeid(signed char))
				{
					value = boost::lexical_cast<std::string>(static_cast<signed int>(iter->second.getData<signed char>()));
				}
				else if(type == typeid(unsigned short))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<unsigned short>());
				}
				else if(type == typeid(signed short))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<signed short>());
				}
				else if(type == typeid(unsigned int))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<unsigned int>());
				}
				else if(type == typeid(signed int))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<signed int>());
				}
				else if(type == typeid(unsigned long long))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<unsigned long long>());
				}
				else if(type == typeid(signed long long))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<signed long long>());
				}
				else if(type == typeid(std::string))
				{
					value = boost::lexical_cast<std::string>(iter->second.getData<std::string>());
				}
				else
				{
					throw std::logic_error("What type are you trying to feed me!?");
				}
				query.append(iter->first).append("` = '").append(value).append("', `");
			}
			query = query.substr(0, query.length() - 3).append(" WHERE `user` = '").append(boost::lexical_cast<std::string>(user)).append("'");
			int rows = this->connector.executeUpdate(query); // <-- Todo: rewrite database wrapper to use exceptions
			if(rows == -1)
			{
				throw std::logic_error("Something bad happened...");
			}
		}

#endif 


PS. I'm aware that the queries aren't protected from injection attacks (or properly un-escaped).
Last edited on
Never worked with databases, but will post anyway..

I don't understand why would you do this at all. Wikipedia says that ORM is needed when you're working with non-scalars, which is clearly not the case, as, while Attribute can store anything, saveAttributes will break if that anything is not a scalar.
Is this just an optimization so that you don't need to do queries every time you use those variables? I wonder if SQL doesn't have similar optimization of its own..

Another thing that baffles me is the way you save things (by generating an SQL query). Isn't there a better way which does not waste time converting scalars to strings, parsing the thing and then converting them back?
Though, what part of this is open to injection attacks? There isn't any user input, is there? Or am I misunderstanding what injection attacks are.
closed account (3hM2Nwbp)
I should have posted a better explanation of what it's for, but my original post used all (8192) of the characters allowed in a post. All in all, what was posted is an unoptimized quick and dirty mess (to see if the concept would actually work).

This is part of a little job that I picked up to help make an existing server-side multiplayer game framework easier to work with. Generally, they are seeing between 400 and 1000 users online, but the way their hand rolled framework is set up, it's a pain to add more features (by having to modify the saving routine, the loading routine, the user model, and data transfer routines). The idea behind the dynamic cache is to prevent the modification of the source code, prevent a recompilation, and hopefully eliminate the possibility of a bug sneaking in. To add a persistable attribute to the game, all that is needed is to create a new column in the database.

Though, what part of this is open to injection attacks?
1
2
3
4
value = boost::lexical_cast<std::string>(iter->second.getData<std::string>());
// iter->first = filtered_chat_log
// iter->second = DROP TABLE
// ^^ I didn't write the framework, but they claim chat logs really help to find cheaters. 



saveAttributes will break if anything is not a scalar

Yes, it will...however the creation of attributes is something that only the loadAttributes method should do (preventing the creation of any non-scalar attributes. I assume that ORM operates by breaking down non-scalar types into their basic types and then generating the appropriate query to insert them into the database.

Isn't there a better way (to save things?)

I'm sure there is, perhaps a prepared statement...but I don't really have any experience with professional database management so I have some reading to do.
Last edited on
I just wanted to mention this:
By linking with the ODB runtime libraries (directly or indirectly, statically or dynamically, at compile time or runtime), your application is subject to the terms of the GPL version 2 which requires that you release the source code of your application if and when you distribute it.
This is correct only in the case that code is distributed. If no code is distributed, as would be the case with a server, no source code needs to be released. The GPL wrapping its disgusting little fingers around the program may still be problem even in this case (for example, if closed source licences for the server are expected to be sold at some point in the future, or if the owner just doesn't want to deal with the GPL), but I thought you should know this.
closed account (3hM2Nwbp)
Time to derail the thread.

Sometimes I really want to make a license that restricts the ability for derivative works to be licensed under the GPL vX...I really agree with this person when it comes to free software: http://dewimorgan.livejournal.com/21650.html

Maybe then I'll start writing more non-infectable public domain code.
I'm glad I don't use the GPL. I knew it was ridiculous but this is too far.

Also, does that apply to glibc?
Luc Lieber:
http://www.dwheeler.com/essays/gpl-compatible.html
It's simply not an efficient method.
[EDIT]I really like that link you posted.[/EDIT]

chrisname:
The GPLv3 is even worse. That exception I made earlier with server-side software doesn't apply with it. Version 4 will presumably require some sort of blood price when you try to link to code licenced under it.

About glibc, keep in mind that it was intended as a system library for the GNU OS. It doesn't make sense to make a system library that doesn't allow you to run any software you want in the system.
Last edited on
the GPL is viral and parasitic by design, while claiming to "protect our interests". However, people using such emotive words are generally regarded as frothing at the mouth.

But I don't know of a better word than viral to describe "a clause that either prevents merging of two codebases with differing licenses, or causes the other license to fall out of effect, and which prevents all subsequent users of the code from adding or removing any restrictions or alternate licenses."


Then I think that the usual copyright law is viral in nature too. So, copyright law can prevent me, The User, the one that Actually Does The Work, to make a copy of Windows/Word/Photoshop and proceed with my usual pressing work.

So, if the GPL is viral onto software companies, then the usual copyright law is viral onto Regular Users.

And I don't know of any better term than parasitic to describe a clause which means "any GPL fork of an open source project originally released under another license, can take any improvements subsequently made in the other fork, but the other fork can't take any of the GPL improvements."


Then I don't know of any better term than parasitic to have a program on my computer that cannot be modified by the User at All.

To me, complaining from the GPL is being a "cry-baby" - you are always free to go ahead and buy a professional library to suit your needs.
Last edited on
Well, I think that the usual copyright law is viral in nature too.
The author makes that point further down.
Copyright is inherently viral, and any license, even the most liberal, like BSD, are viral to some extent. Many of the problems in the GPL come about because it is based on copyright, and many of the arguments here apply to all copyright licenses.
To be honest, I don't understand this line of reasoning. I don't see how permissive licences are viral at all. Since you seem to have independently come to the same conclusion, maybe you could enlighten me?

And I don't know of any better term than parasitic to have a program on my computer that cannot be modified by the User at All.

To me, complaining from the GPL is being a "cry-baby" - you are always free to go ahead and buy a professional library to suit your needs.
You seem to have missed the point. He isn't talking about closed source in that paragraph at all. What he's saying is that the GPL encourages embrace extend and extinguish (or rather, fork extend and extinguish) against open source alternatives, which traditionally hacker organizations such as the FSF were supposedly against.
See http://web.archive.org/web/20030518160658/http://www.winehq.com/hypermail/wine-license/2002/03/0134.html for a more detailed explanation.
Last edited on
To be honest, I don't understand this line of reasoning. I don't see how permissive licences are viral at all. Since you seem to have independently come to the same conclusion, maybe you could enlighten me?


I was ironizing of the cited author. Also, I think he switched the paraziting and viral adjectives. I find his opening paragraphs don't make any sense.


You seem to have missed the point. He isn't talking about closed source in that paragraph at all. What he's saying is that the GPL encourages embrace extend and extinguish (or rather, fork extend and extinguish) against open source alternatives


I agree, however I do not see it as a bad feature of the license. I think that companies that wish to sell us software should write everything themselves (or buy it among one another). I find that the GPL argument is the User vs The Software Companies.

Companies/Corporations basically wanted to quietly take us, the rest of mankind, for complete idiots, and in some sense they succeeded (example: Windows, Word).

At some point the technically able users assembled together and made a pact - called the GPL - that ensures that we have code that companies cannot use (to resell to us, except violating licenses), just like they have code that we, the Users, can't use (except overpriced or pirated compiled+linked binaries).

I see the problem as Software Users vs Software Companies, and the GPL is the users' main weapon.
Last edited on
I was ironizing of the cited author.
I was afraid that would be the case.

I think that companies that wish to sell us software should write everything themselves
This is just silly. A common problem shouldn't need to be solved more than once. After that you're just wasting everyone's time; the developers' time in finding and fixing bugs, and the users' time in dealing with the bugs.
Great software stands on the shoulders of giants.

I find that the GPL argument is the User vs The Software Companies.
More like "GPL software developers vs. every other type of developer". The user isn't a factor since they can't use the code anyway.

At some point the technically able users assembled together and made a pact - called the GPL - that ensures that we have code that companies cannot use
Coincidentally, this created code that other open source software can't use without submitting to the GPL. Which brings me back to the "standing on giants" part. The Man isn't the only one duplicating effort.
I actually agree with all the non-adjective containing statements you made (i.e. we agree on the factual situation, just not on which aspects of it are good/bad).


A common problem shouldn't need to be solved more than once. After that you're just wasting everyone's time;

Agree, but this is not how life works.

How do you comment on the fact that, for example, I can't use Mathematica's code freely. If 3 years ago, I had free and proper access to their C/C++ code for polynomials/matrices, plotting on the screen, etc., I probably wouldn't have had to reinvent the wheel. Assuming I am not a hard-headed prick that does everything himself anyways, I would had proceeded with what is supposed to be my mathematics specialty right away. Probably, by now I would be 1-2 years ahead of where I am right now.
Last edited on
I did say "common problem". For example, decoding compressed audio is a common problem.

But to properly answer your question, IMO access to information is an all or nothing thing. If you don't want to make it available, fine; but if you do make it available you shouldn't impose restrictions on its use. To me, closed source software and permissive or freer licences are both perfectly ethical. If Wolfram didn't want you using their code then that's just the way it is. That information never existed, as far as you know. It'd be worse to sell your soul to the devil for access to that information.

More pragmatically, couldn't you use the API to give instructions to the server (or "kernel" as it's called)?
Yes, I could, and same with MAPLE, but I never legally owned any of those (I have tried them (on MAPLE I even did actual work) of course using pirated versions).

However, equating using the GPL to selling your soul is a bit... oh well, Stallman does sound like a bad guy, but the devil should be looked for in the Microsoft camp ...
Last edited on
Well, I wasn't talking specifically about the GPL. EULAs are closer to what I'm talking about.

I never legally owned any of those
Oh, in that case you don't even have the right to complain. And I say this as someone with equally shady practices.
Topic archived. No new replies allowed.