libtabula

Check-in [c78d403f45]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Refactored the FieldType implementation details to reduce the amount of code duplication between the base type and the old TypeInfo class, greatly increasing the clarity of the code. This version is gives the same semi-broken test results as the previous checkin on this branch, so it's a good refactoring. :)
Timelines: family | ancestors | descendants | both | mysql-type-info
Files: files | file ages | folders
SHA1: c78d403f45e0d2061d86d3947a544da188726874
User & Date: etr 2015-08-12 02:19:34
Context
2015-08-12
21:46
Merged Wishlist.md change in check-in: a6baa231d9 user: etr tags: mysql-type-info
02:19
Refactored the FieldType implementation details to reduce the amount of code duplication between the base type and the old TypeInfo class, greatly increasing the clarity of the code. This version is gives the same semi-broken test results as the previous checkin on this branch, so it's a good refactoring. :) check-in: c78d403f45 user: etr tags: mysql-type-info
2015-08-11
11:40
The std::type_info to FieldType mapping mechanism was purposely self-limiting itself to looking up "best guess" entries only, on the theory that since moving from MySQL C API data type enums to libtabula generic type enums we lost the uniquenss of the {FieldType::Base, FieldType::Flags} pair, that we should only look through the subset that has unique pairs. However, that logic only works when mapping from libtabula values *to* C++ data types, not the other way around, since all of the C++ type_info values in that table *are* unique.

Therefore, removed the check on the new "best guess" flag in FieldType::TypeMap::operator[] since it isn't helping us. Maybe we will use this flag later, if we add the reverse mapping; or, maybe we'll remove it on YAGNI grounds. check-in: 28f9d01b73 user: etr tags: mysql-type-info

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/field_type.cpp.

34
35
36
37
38
39
40



41
42
43
44
45
46
47
..
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

183
184
185











186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

























































206
207
208
209






210
211
212
213

#include <string>

using namespace std;

namespace libtabula {




// This table maps generic C++ type information to libtabula specific
// type information.  It is part of the transformation that allows
// plain C++ data types to map to driver-specific data types.
//
// The second half of the table parallels the first, to handle null-able
// versions of the types in the first half.  This is required because
// SQL's 'null' concept does not map neatly into the C++ type system, so
................................................................................
// where there may be multiple sizes.  We use the largest "native" size.
// 
// This table does not necessarily honor fine type distinctions made at
// the DBMS layer.  For example, we paper over the UNSIGNED FLOAT mess.
//
// This table is inverted for reverse lookup as the static type_map_
// object.
typedef FieldType::TypeInfo FTTI;
const FTTI FieldType::types_[] = {
	// Basic non-nullable type set
	FTTI("TINYINT NOT NULL", typeid(sql_tinyint),
			FieldType::ft_integer, FieldType::tf_default, false),
	FTTI("TINYINT UNSIGNED NOT NULL", typeid(sql_tinyint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	FTTI("SMALLINT NOT NULL", typeid(sql_smallint),
			FieldType::ft_integer, FieldType::tf_default, false),
	FTTI("SMALLINT UNSIGNED NOT NULL", typeid(sql_smallint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	FTTI("MEDIUMINT NOT NULL", typeid(sql_mediumint),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	FTTI("MEDIUMINT UNSIGNED NOT NULL", typeid(sql_mediumint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	FTTI("INT NOT NULL", typeid(sql_int), FieldType::ft_integer),
	FTTI("INT UNSIGNED NOT NULL", typeid(sql_int_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned),
	FTTI("BIGINT NOT NULL", typeid(sql_bigint), 
			FieldType::ft_integer, FieldType::tf_default, false),
	FTTI("BIGINT UNSIGNED NOT NULL", typeid(sql_bigint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	FTTI("FLOAT NOT NULL", typeid(sql_float), 
			FieldType::ft_real, FieldType::tf_default, false),
	FTTI("FLOAT UNSIGNED NOT NULL", typeid(sql_float),
			FieldType::ft_real, FieldType::tf_unsigned, false),
	FTTI("DOUBLE NOT NULL", typeid(sql_double), FieldType::ft_real),
	FTTI("DOUBLE UNSIGNED NOT NULL", typeid(sql_double),
			FieldType::ft_real, FieldType::tf_unsigned),
	FTTI("DECIMAL NOT NULL", typeid(sql_decimal), FieldType::ft_decimal),
	FTTI("TIMESTAMP NOT NULL", typeid(sql_timestamp), FieldType::ft_timestamp),
	FTTI("DATE NOT NULL", typeid(sql_date), FieldType::ft_date),
	FTTI("TIME NOT NULL", typeid(sql_time), FieldType::ft_time),
	FTTI("DATETIME NOT NULL", typeid(sql_datetime), FieldType::ft_datetime),
	FTTI("ENUM NOT NULL", typeid(sql_enum), FieldType::ft_enum),
	FTTI("SET NOT NULL", typeid(sql_set), FieldType::ft_set),
	FTTI("TINYBLOB NOT NULL", typeid(sql_tinyblob),
			FieldType::ft_blob, FieldType::tf_default, false),
	FTTI("MEDIUMBLOB NOT NULL", typeid(sql_mediumblob), 
			FieldType::ft_blob, FieldType::tf_default, false),
	FTTI("LONGBLOB NOT NULL", typeid(sql_longblob),
			FieldType::ft_blob, FieldType::tf_default, false),
	FTTI("BLOB NOT NULL", typeid(sql_blob), FieldType::ft_blob),
	FTTI("VARCHAR NOT NULL", typeid(sql_varchar), FieldType::ft_text),
	FTTI("CHAR NOT NULL", typeid(sql_char), 
			FieldType::ft_text, FieldType::tf_default, false),
	FTTI("NULL NOT NULL", typeid(void),
			FieldType::ft_null, FieldType::tf_default),

	// Nullable versions of above
	FTTI("TINYINT NULL", typeid(Null<sql_tinyint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	FTTI("TINYINT UNSIGNED NULL", typeid(Null<sql_tinyint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	FTTI("SMALLINT NULL", typeid(Null<sql_smallint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	FTTI("SMALLINT UNSIGNED NULL", typeid(Null<sql_smallint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	FTTI("MEDIUMINT NULL", typeid(Null<sql_mediumint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	FTTI("MEDIUMINT UNSIGNED NULL", typeid(Null<sql_mediumint_unsigned>), 
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	FTTI("INT NULL", typeid(Null<sql_int>),
			FieldType::ft_integer, FieldType::tf_null),
	FTTI("INT UNSIGNED NULL", typeid(Null<sql_int_unsigned>),
			FieldType::ft_integer, FieldType::tf_null | FieldType::tf_unsigned),
	FTTI("BIGINT NULL", typeid(Null<sql_bigint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	FTTI("BIGINT UNSIGNED NULL", typeid(Null<sql_bigint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	FTTI("FLOAT NULL", typeid(Null<sql_float>),
			FieldType::ft_real, FieldType::tf_null, false),
	FTTI("FLOAT UNSIGNED NULL", typeid(Null<sql_float>),
			FieldType::ft_real,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	FTTI("DOUBLE NULL", typeid(Null<sql_double>),
			FieldType::ft_real, FieldType::tf_null),
	FTTI("DOUBLE UNSIGNED NULL", typeid(Null<sql_double>),
			FieldType::ft_real, FieldType::tf_null | FieldType::tf_unsigned),
	FTTI("DECIMAL NULL", typeid(Null<sql_decimal>),
			FieldType::ft_decimal, FieldType::tf_null),
	FTTI("TIMESTAMP NULL", typeid(Null<sql_timestamp>),
			FieldType::ft_timestamp),
	FTTI("DATE NULL", typeid(Null<sql_date>),
			FieldType::ft_date, FieldType::tf_null),
	FTTI("TIME NULL", typeid(Null<sql_time>),
			FieldType::ft_time, FieldType::tf_null),
	FTTI("DATETIME NULL", typeid(Null<sql_datetime>),
			FieldType::ft_datetime, FieldType::tf_null),
	FTTI("ENUM NULL", typeid(Null<sql_enum>),
			FieldType::ft_enum, FieldType::tf_null),
	FTTI("SET NULL", typeid(Null<sql_set>),
			FieldType::ft_set, FieldType::tf_null),
	FTTI("TINYBLOB NULL", typeid(Null<sql_tinyblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	FTTI("MEDIUMBLOB NULL", typeid(Null<sql_mediumblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	FTTI("LONGBLOB NULL", typeid(Null<sql_longblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	FTTI("BLOB NULL", typeid(Null<sql_blob>),
			FieldType::ft_blob, FieldType::tf_null),
	FTTI("VARCHAR NULL", typeid(Null<sql_varchar>),
			FieldType::ft_text, FieldType::tf_null),
	FTTI("CHAR NULL", typeid(Null<sql_char>),
			FieldType::ft_text, FieldType::tf_null, false),
	FTTI("NULL NOT NULL", typeid(Null<void>),
			FieldType::ft_null, FieldType::tf_default),
};

const int FieldType::num_types_ =
		sizeof(FieldType::types_) / sizeof(FieldType::types_[0]);

const FieldType::TypeMap FieldType::type_map_;


#if !defined(DOXYGEN_IGNORE)
// Doxygen will not generate documentation for this section.












FieldType::TypeMap::TypeMap()
{
	for (map_type::mapped_type i = 0; i < num_types_; ++i) {
		map_[FieldType::types_[i].c_type_] = i;
	}
}

FieldType::TypeMap::map_type::mapped_type 
FieldType::TypeMap::operator [](const std::type_info& ti) const
{
	// Try for an exact match on the C++ std::type
	map_type::const_iterator it = map_.find(&ti);
	if (it != map_.end()) {
		return it->second;
	}
	else {
		// Can't find it.  Caller must be passing a C++ data type that
		// simply isn't represented in the types_ array.  Wah.
		std::ostringstream outs;
		outs << "Failed to find libtabula type info for " << ti.name();

























































		throw TypeLookupFailed(outs.str());
	}
}







#endif // !defined(DOXYGEN_IGNORE)

} // end namespace libtabula








>
>
>







 







|
|

|

|

|

|

|

|

|
|

|

|

|

|

|
|

|
|
|
|
|
|
|
|

|

|

|
|
|

|



|

|


|

|


|

|


|

|

|

|


|

|


|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|



|
|

<

>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
<
|
<
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>
>
>
>
>
>




34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

206

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

#include <string>

using namespace std;

namespace libtabula {

#if !defined(DOXYGEN_IGNORE)
// Doxygen will not generate documentation for this section.

// This table maps generic C++ type information to libtabula specific
// type information.  It is part of the transformation that allows
// plain C++ data types to map to driver-specific data types.
//
// The second half of the table parallels the first, to handle null-able
// versions of the types in the first half.  This is required because
// SQL's 'null' concept does not map neatly into the C++ type system, so
................................................................................
// where there may be multiple sizes.  We use the largest "native" size.
// 
// This table does not necessarily honor fine type distinctions made at
// the DBMS layer.  For example, we paper over the UNSIGNED FLOAT mess.
//
// This table is inverted for reverse lookup as the static type_map_
// object.
typedef detail::AnnotatedFT AFT;
static const AFT types_[] = {
	// Basic non-nullable type set
	AFT("TINYINT NOT NULL", typeid(sql_tinyint),
			FieldType::ft_integer, FieldType::tf_default, false),
	AFT("TINYINT UNSIGNED NOT NULL", typeid(sql_tinyint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	AFT("SMALLINT NOT NULL", typeid(sql_smallint),
			FieldType::ft_integer, FieldType::tf_default, false),
	AFT("SMALLINT UNSIGNED NOT NULL", typeid(sql_smallint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	AFT("MEDIUMINT NOT NULL", typeid(sql_mediumint),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	AFT("MEDIUMINT UNSIGNED NOT NULL", typeid(sql_mediumint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	AFT("INT NOT NULL", typeid(sql_int), FieldType::ft_integer),
	AFT("INT UNSIGNED NOT NULL", typeid(sql_int_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned),
	AFT("BIGINT NOT NULL", typeid(sql_bigint), 
			FieldType::ft_integer, FieldType::tf_default, false),
	AFT("BIGINT UNSIGNED NOT NULL", typeid(sql_bigint_unsigned),
			FieldType::ft_integer, FieldType::tf_unsigned, false),
	AFT("FLOAT NOT NULL", typeid(sql_float), 
			FieldType::ft_real, FieldType::tf_default, false),
	AFT("FLOAT UNSIGNED NOT NULL", typeid(sql_float),
			FieldType::ft_real, FieldType::tf_unsigned, false),
	AFT("DOUBLE NOT NULL", typeid(sql_double), FieldType::ft_real),
	AFT("DOUBLE UNSIGNED NOT NULL", typeid(sql_double),
			FieldType::ft_real, FieldType::tf_unsigned),
	AFT("DECIMAL NOT NULL", typeid(sql_decimal), FieldType::ft_decimal),
	AFT("TIMESTAMP NOT NULL", typeid(sql_timestamp), FieldType::ft_timestamp),
	AFT("DATE NOT NULL", typeid(sql_date), FieldType::ft_date),
	AFT("TIME NOT NULL", typeid(sql_time), FieldType::ft_time),
	AFT("DATETIME NOT NULL", typeid(sql_datetime), FieldType::ft_datetime),
	AFT("ENUM NOT NULL", typeid(sql_enum), FieldType::ft_enum),
	AFT("SET NOT NULL", typeid(sql_set), FieldType::ft_set),
	AFT("TINYBLOB NOT NULL", typeid(sql_tinyblob),
			FieldType::ft_blob, FieldType::tf_default, false),
	AFT("MEDIUMBLOB NOT NULL", typeid(sql_mediumblob), 
			FieldType::ft_blob, FieldType::tf_default, false),
	AFT("LONGBLOB NOT NULL", typeid(sql_longblob),
			FieldType::ft_blob, FieldType::tf_default, false),
	AFT("BLOB NOT NULL", typeid(sql_blob), FieldType::ft_blob),
	AFT("VARCHAR NOT NULL", typeid(sql_varchar), FieldType::ft_text),
	AFT("CHAR NOT NULL", typeid(sql_char), 
			FieldType::ft_text, FieldType::tf_default, false),
	AFT("NULL NOT NULL", typeid(void),
			FieldType::ft_null, FieldType::tf_default),

	// Nullable versions of above
	AFT("TINYINT NULL", typeid(Null<sql_tinyint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	AFT("TINYINT UNSIGNED NULL", typeid(Null<sql_tinyint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	AFT("SMALLINT NULL", typeid(Null<sql_smallint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	AFT("SMALLINT UNSIGNED NULL", typeid(Null<sql_smallint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	AFT("MEDIUMINT NULL", typeid(Null<sql_mediumint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	AFT("MEDIUMINT UNSIGNED NULL", typeid(Null<sql_mediumint_unsigned>), 
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	AFT("INT NULL", typeid(Null<sql_int>),
			FieldType::ft_integer, FieldType::tf_null),
	AFT("INT UNSIGNED NULL", typeid(Null<sql_int_unsigned>),
			FieldType::ft_integer, FieldType::tf_null | FieldType::tf_unsigned),
	AFT("BIGINT NULL", typeid(Null<sql_bigint>),
			FieldType::ft_integer, FieldType::tf_null, false),
	AFT("BIGINT UNSIGNED NULL", typeid(Null<sql_bigint_unsigned>),
			FieldType::ft_integer,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	AFT("FLOAT NULL", typeid(Null<sql_float>),
			FieldType::ft_real, FieldType::tf_null, false),
	AFT("FLOAT UNSIGNED NULL", typeid(Null<sql_float>),
			FieldType::ft_real,
			FieldType::tf_null | FieldType::tf_unsigned, false),
	AFT("DOUBLE NULL", typeid(Null<sql_double>),
			FieldType::ft_real, FieldType::tf_null),
	AFT("DOUBLE UNSIGNED NULL", typeid(Null<sql_double>),
			FieldType::ft_real, FieldType::tf_null | FieldType::tf_unsigned),
	AFT("DECIMAL NULL", typeid(Null<sql_decimal>),
			FieldType::ft_decimal, FieldType::tf_null),
	AFT("TIMESTAMP NULL", typeid(Null<sql_timestamp>),
			FieldType::ft_timestamp),
	AFT("DATE NULL", typeid(Null<sql_date>),
			FieldType::ft_date, FieldType::tf_null),
	AFT("TIME NULL", typeid(Null<sql_time>),
			FieldType::ft_time, FieldType::tf_null),
	AFT("DATETIME NULL", typeid(Null<sql_datetime>),
			FieldType::ft_datetime, FieldType::tf_null),
	AFT("ENUM NULL", typeid(Null<sql_enum>),
			FieldType::ft_enum, FieldType::tf_null),
	AFT("SET NULL", typeid(Null<sql_set>),
			FieldType::ft_set, FieldType::tf_null),
	AFT("TINYBLOB NULL", typeid(Null<sql_tinyblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	AFT("MEDIUMBLOB NULL", typeid(Null<sql_mediumblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	AFT("LONGBLOB NULL", typeid(Null<sql_longblob>),
			FieldType::ft_blob, FieldType::tf_null, false),
	AFT("BLOB NULL", typeid(Null<sql_blob>),
			FieldType::ft_blob, FieldType::tf_null),
	AFT("VARCHAR NULL", typeid(Null<sql_varchar>),
			FieldType::ft_text, FieldType::tf_null),
	AFT("CHAR NULL", typeid(Null<sql_char>),
			FieldType::ft_text, FieldType::tf_null, false),
	AFT("NULL NOT NULL", typeid(Null<void>),
			FieldType::ft_null, FieldType::tf_default),
};

// Number of elements in types_[]
static const int num_types_ = sizeof(types_) / sizeof(types_[0]);



// Set up an "index" into types_[] keyed on std::type_info, to make
// the FieldType(type_info) conversion ctor faster.
class TypeMap
{
private:
	struct Cmp
	{
		bool operator() (const type_info* lhs, const type_info* rhs) const
				{ return lhs < rhs; }
	};
	typedef map<const type_info*, size_t, Cmp> map_type;

	map_type map_;

public:
	TypeMap()
	{
		for (map_type::mapped_type i = 0; i < num_types_; ++i) {
			map_[types_[i].c_type_] = i;
		}
	}

	const map_type::mapped_type operator [](const type_info& ti) const

	{
		// Try for an exact match on the C++ std::type_info value
		map_type::const_iterator it = map_.find(&ti);
		if (it != map_.end()) {
			return it->second;
		}
		else {
			// Can't find it.  Caller must be passing a C++ data type
			// that simply isn't represented in the types_ array.  Wah.
			ostringstream outs;
			outs << "Failed to find libtabula type info for " << ti.name();
			throw TypeLookupFailed(outs.str());
		}
	}
};
static const TypeMap type_map_;


// std::type_info conversion ctor.  Looks up the statically-defined
// libtabula type info in types_[] and copies that.
FieldType::FieldType(const type_info& t)
{
	size_t i = type_map_[t];
	base_type_ = types_[i].base_type_;
	flags_ = types_[i].flags_;
}


// "Annotate" this type by looking up the C++ and SQL type info in the
// types_[] table based on our libtabula {Base,Flags} pair.
const AFT&
FieldType::annotate() const
{
	// Make sure we got fully-initted first.
	if (base_type_ == ft_unsupported) {
		throw TypeLookupFailed("FieldType::annotate() called before "
				"object fully initted");
	}

	// Try to find an entry in types_[] matching our {Base,Flags} pair
	int guess = -1;
	for (size_t i = 0; i < num_types_; ++i) {
		const AFT& cand = types_[i];
		if (base_type_ == cand.base_type_) {
			// Found a possible match.
			if (flags_ == cand.flags_) {
				// It's a good match, but is it the best?
				guess = i;
				if (cand.best_guess_) return cand;
			}
			else if (cand.flags_ & flags_ == cand.flags_) {
				// All flags on the candidate are present in the
				// caller's value, so keep this one in mind, but
				// don't go with it unless we find no better option.
				guess = i;
			}
			// else, it's only a weak match; hold out for better
		}
	}

	// Did we find one?
	if (guess >= 0) {
		return types_[guess];
	}
	else {
		ostringstream outs;
		outs << "Failed to find C++ and SQL type info for libtabula "
				"type {" << base_type_ << ',' << flags_ << '}';
		throw TypeLookupFailed(outs.str());
	}
}


// Pass calls down to our equivalent AnnotatedFT object
const char* FieldType::name() const { return annotate().c_type_->name(); }
const char* FieldType::sql_name() const { return annotate().sql_name_; }
const std::type_info& FieldType::c_type() const { return *annotate().c_type_; }

#endif // !defined(DOXYGEN_IGNORE)

} // end namespace libtabula

Changes to src/field_type.h.

41
42
43
44
45
46
47



48
49
50
51
52
53
54
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184





































185
186
187
188
189
190
191
...
207
208
209
210
211
212
213
214
215
216
217









218
219
220
221
222




223
224
225
226




227
228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290

291
292
293
294
295
296
297

#include <map>
#include <sstream>
#include <typeinfo>

namespace libtabula {





/// \brief SQL field type information
///
/// \internal Used within libtabula for mapping SQL types to C++ types
/// and vice versa.
class LIBTABULA_EXPORT FieldType
{
................................................................................
	///
	/// This tries to map a C++ type to the closest libtabula data type.
	/// It is necessarily somewhat approximate.  For one thing, we
	/// ignore integer signedness.  We also make the fully-warranted
	/// assumption that C++ hasn't grown a SQL-like "nullable" type
	/// attribute between the time this code was written and the time
	/// your compiler was last improved.
	FieldType(const std::type_info& t)
	{
		size_t i = type_map_[t];
		base_type_ = types_[i].base_type_;
		flags_ = types_[i].flags_;
	}

	/// \brief Assign another FieldType object to this object
	FieldType& operator =(const FieldType& t)
	{
		base_type_ = t.base_type_;
		flags_ = t.flags_;
		return *this;
	}

	/// \brief Returns an implementation-defined name of the C++ type.
	///
	/// Returns the name that would be returned by typeid().name() for
	/// the C++ type associated with the SQL type.
	const char* name() const { return type_info().c_type_->name(); }

	/// \brief Returns the name of the SQL type.
	///
	/// Returns the SQL name for the type.
	const char* sql_name() const { return type_info().sql_name_; }

	/// \brief Returns the type_info for the C++ type associated with
	/// the SQL type.
	///
	/// Returns the C++ type_info record corresponding to the SQL type.
	const std::type_info& c_type() const { return *type_info().c_type_; }

	/// \brief Returns the libtabula data type enum for this object.
	///
	/// Our return value may simply be the Base value we got in our
	/// ctor, but it could also be a conversion from another form,
	/// either C++ type info via our std::type_info ctor or C DBMS
	/// API type info from a subclass ctor.
	///
	/// This does not encode null-ness.
	const Base base_type() const { return base_type_; }






































	/// \brief Returns true if the SQL type is of a type that needs to
	/// be quoted for syntactically correct SQL.
	bool quote_q() const
	{
		return	base_type_ == ft_blob ||
				base_type_ == ft_date ||
................................................................................
	/// \brief Return an opaque composite value that uniquely identifies
	/// this field type.
	///
	/// Though the implementation is right here to be examined, do not
	/// depend on the format of the value.  It could change.
	unsigned short id() const { return (flags_ << 8) | base_type_; }

	// Used in the global static data structures that map libtabula data
	// types and from {Base, Flag} enum pairs.  That in turn is how
	// you can construct a FieldType object and call c_type() and such
	// on it, yielding a value you did not explicitly pass to the ctor.









	//
	// \internal This is public only because the cpp file instantiates
	// \c types_, a static array of these.  Library end-user code
	// shouldn't use this directly.
	class TypeInfo




	{
	public:
		TypeInfo& operator=(const TypeInfo& other);
		




		TypeInfo(const char* s = 0, const std::type_info& t = typeid(void),
				Base bt = ft_unsupported, unsigned int f = tf_default,
				bool bg = true) :

		sql_name_(s),
		c_type_(&t),
		base_type_(bt),
		flags_(f),
		best_guess_(bg)
		{
		}

		unsigned short id() const { return (flags_ << 8) | base_type_; }

		bool is_default() const { return flags_ == FieldType::tf_default; }
		bool is_null() const { return flags_ & FieldType::tf_null; }
		bool is_unsigned() const { return flags_ & FieldType::tf_unsigned; }

		const char* sql_name_;
		const std::type_info* c_type_;
		const Base base_type_;
		const unsigned int flags_;
		const bool best_guess_;		// YAGNI?  Unused by TypeMap::operator[]
	};

protected:
	//// Subclass interface
	/// \brief Look up the TypeInfo object corresponding to our
	/// {Base, Flag} pair.
	const TypeInfo& type_info() const
	{
		return types_[base_type_];
	}

private:
	// Helper class for mapping data types to and from our enums above
	class LIBTABULA_EXPORT TypeMap
	{
	private:
		friend class FieldType;

		struct Cmp
		{
			bool operator() (const std::type_info* lhs,
					const std::type_info* rhs) const
					{ return lhs < rhs; }
		};
		typedef std::map<const std::type_info*, size_t, Cmp> map_type;

		TypeMap();
		map_type::mapped_type operator [](const std::type_info& ti) const;

		map_type map_;
	};

	// Data structures to map {Base, Type} pairs to and from libtabula
	// C++ data types.
	static const TypeInfo types_[];
	static const int num_types_;
	static const TypeMap type_map_;

	// Internal data
	Base base_type_;
	unsigned int flags_;
};


/// \brief Returns true if two FieldType objects are equal.
inline bool operator ==(const FieldType& lhs, const FieldType& rhs)
{
	return lhs.id() == rhs.id();
}








>
>
>







 







|
<
<
<
<
<









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
<
>
>
>
>
>
>
>
>
>
|
<
<
<
<
>
>
>
>


|

>
>
>
>
|
|

>


<
<




<
<
<
<
<
<


<
<
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
140
141
142
143
144
145
146
147





148
149
150
151
152
153
154
155
156

















157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
...
225
226
227
228
229
230
231
232
233
234

235
236
237
238
239
240
241
242
243
244




245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262


263
264
265
266






267
268


269
270









































271
272
273
274
275
276
277
278

#include <map>
#include <sstream>
#include <typeinfo>

namespace libtabula {

namespace detail {
	class AnnotatedFT;
}

/// \brief SQL field type information
///
/// \internal Used within libtabula for mapping SQL types to C++ types
/// and vice versa.
class LIBTABULA_EXPORT FieldType
{
................................................................................
	///
	/// This tries to map a C++ type to the closest libtabula data type.
	/// It is necessarily somewhat approximate.  For one thing, we
	/// ignore integer signedness.  We also make the fully-warranted
	/// assumption that C++ hasn't grown a SQL-like "nullable" type
	/// attribute between the time this code was written and the time
	/// your compiler was last improved.
	FieldType(const std::type_info& t);






	/// \brief Assign another FieldType object to this object
	FieldType& operator =(const FieldType& t)
	{
		base_type_ = t.base_type_;
		flags_ = t.flags_;
		return *this;
	}


















	/// \brief Returns the libtabula data type enum for this object.
	///
	/// Our return value may simply be the Base value we got in our
	/// ctor, but it could also be a conversion from another form,
	/// either C++ type info via our std::type_info ctor or C DBMS
	/// API type info from a subclass ctor.
	///
	/// This does not encode null-ness.
	const Base base_type() const { return base_type_; }

	/// \brief Returns true if this type describes a SQL column with a
	/// default value.
	///
	/// Always returns false if the object is storing information about
	/// a SQL value on a particular row, rather than column info.
	bool is_default() const { return flags_ == FieldType::tf_default; }

	/// \brief Returns true if this type describes either a null-able SQL
	/// column or a SQL value that is NULL.
	bool is_null() const { return flags_ & FieldType::tf_null; }

	/// \brief Returns true if this type describes an SQL column that
	/// cannot be negative.
	///
	/// Various SQL DBMSes interpret this differently.  Some don't have
	/// a strong notion of unsigned-ness (e.g. SQLite) while others take
	/// the concept rather too far (IMHO) allowing perversions like
	/// "unsigned float." (e.g. MySQL/MariaDB)
	bool is_unsigned() const { return flags_ & FieldType::tf_unsigned; }

	/// \brief Returns an implementation-defined name of the C++ type.
	///
	/// Returns the name that would be returned by typeid().name() for
	/// the C++ type associated with the SQL type.
	const char* name() const;

	/// \brief Returns the name of the SQL type.
	///
	/// Returns the SQL name for the type.
	const char* sql_name() const;

	/// \brief Returns the type_info for the C++ type associated with
	/// the SQL type.
	///
	/// Returns the C++ type_info record corresponding to the SQL type.
	const std::type_info& c_type() const;

	/// \brief Returns true if the SQL type is of a type that needs to
	/// be quoted for syntactically correct SQL.
	bool quote_q() const
	{
		return	base_type_ == ft_blob ||
				base_type_ == ft_date ||
................................................................................
	/// \brief Return an opaque composite value that uniquely identifies
	/// this field type.
	///
	/// Though the implementation is right here to be examined, do not
	/// depend on the format of the value.  It could change.
	unsigned short id() const { return (flags_ << 8) | base_type_; }

private:
	//// Internal support functions
	const detail::AnnotatedFT& annotate() const;


	//// Internal data
	Base base_type_;
	unsigned int flags_;
};

namespace detail {
	/// \brief An extension of FieldType, adding several values that are
	/// implicitly associated with our parent's {Base, Flag} pair.
	///




	/// \internal This type is not for use by end-user code.  It is
	/// public only because libtabula creates a static array of these
	/// for use by FieldType to use in various lookups.
	class AnnotatedFT : public FieldType
	{
	public:
		AnnotatedFT& operator=(const AnnotatedFT& other);
		
		// FIXME: Reorder parameter list to match FieldType.  It's only
		// in this order to minimize diffs in the types_[] definition
		// relative to MySQL++.  Once that table settles down, we can
		// do a big-bang changeover.
		AnnotatedFT(const char* s, const std::type_info& t,
				Base bt, unsigned int f = tf_default,
				bool bg = true) :
		FieldType(bt, f),
		sql_name_(s),
		c_type_(&t),


		best_guess_(bg)
		{
		}







		const char* sql_name_;
		const std::type_info* c_type_;


		const bool best_guess_;
	};









































} // end namespace libtabula::detail

/// \brief Returns true if two FieldType objects are equal.
inline bool operator ==(const FieldType& lhs, const FieldType& rhs)
{
	return lhs.id() == rhs.id();
}