1//
2// MySQLException.cpp
3//
4// Library: SQL/MySQL
5// Package: MySQL
6// Module: ResultMetadata
7//
8// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/SQL/MySQL/ResultMetadata.h"
16#include "Poco/SQL/MySQL/MySQLException.h"
17#include <cstring>
18
19namespace
20{
21 class ResultMetadataHandle
22 /// Simple exception-safe wrapper
23 {
24 public:
25
26 explicit ResultMetadataHandle(MYSQL_STMT* stmt)
27 {
28 h = mysql_stmt_result_metadata(stmt);
29 }
30
31 ~ResultMetadataHandle()
32 {
33 if (h)
34 {
35 mysql_free_result(h);
36 }
37 }
38
39 operator MYSQL_RES* ()
40 {
41 return h;
42 }
43
44 private:
45
46 MYSQL_RES* h;
47 };
48
49 std::size_t fieldSize(const MYSQL_FIELD& field)
50 /// Convert field MySQL-type and field MySQL-length to actual field length
51 {
52 switch (field.type)
53 {
54 case MYSQL_TYPE_TINY: return sizeof(char);
55 case MYSQL_TYPE_SHORT: return sizeof(short);
56 case MYSQL_TYPE_INT24:
57 case MYSQL_TYPE_LONG: return sizeof(Poco::Int32);
58 case MYSQL_TYPE_FLOAT: return sizeof(float);
59 case MYSQL_TYPE_DOUBLE: return sizeof(double);
60 case MYSQL_TYPE_LONGLONG: return sizeof(Poco::Int64);
61
62 case MYSQL_TYPE_DATE:
63 case MYSQL_TYPE_TIME:
64 case MYSQL_TYPE_DATETIME:
65 return sizeof(MYSQL_TIME);
66
67 case MYSQL_TYPE_DECIMAL:
68 case MYSQL_TYPE_NEWDECIMAL:
69 case MYSQL_TYPE_STRING:
70 case MYSQL_TYPE_VAR_STRING:
71 case MYSQL_TYPE_TINY_BLOB:
72 case MYSQL_TYPE_MEDIUM_BLOB:
73 case MYSQL_TYPE_LONG_BLOB:
74 case MYSQL_TYPE_BLOB:
75 return field.length;
76
77 default:
78 throw Poco::SQL::MySQL::StatementException("unknown field type");
79 }
80 }
81
82 Poco::SQL::MetaColumn::ColumnDataType fieldType(const MYSQL_FIELD& field)
83 /// Convert field MySQL-type to Poco-type
84 {
85 bool unsig = ((field.flags & UNSIGNED_FLAG) == UNSIGNED_FLAG);
86
87 switch (field.type)
88 {
89 case MYSQL_TYPE_TINY:
90 if (unsig) return Poco::SQL::MetaColumn::FDT_UINT8;
91 return Poco::SQL::MetaColumn::FDT_INT8;
92
93 case MYSQL_TYPE_SHORT:
94 if (unsig) return Poco::SQL::MetaColumn::FDT_UINT16;
95 return Poco::SQL::MetaColumn::FDT_INT16;
96
97 case MYSQL_TYPE_INT24:
98 case MYSQL_TYPE_LONG:
99 if (unsig) return Poco::SQL::MetaColumn::FDT_UINT32;
100 return Poco::SQL::MetaColumn::FDT_INT32;
101
102 case MYSQL_TYPE_FLOAT:
103 return Poco::SQL::MetaColumn::FDT_FLOAT;
104
105 case MYSQL_TYPE_DECIMAL:
106 case MYSQL_TYPE_NEWDECIMAL:
107 case MYSQL_TYPE_DOUBLE:
108 return Poco::SQL::MetaColumn::FDT_DOUBLE;
109
110 case MYSQL_TYPE_LONGLONG:
111 if (unsig) return Poco::SQL::MetaColumn::FDT_UINT64;
112 return Poco::SQL::MetaColumn::FDT_INT64;
113
114 case MYSQL_TYPE_DATE:
115 return Poco::SQL::MetaColumn::FDT_DATE;
116
117 case MYSQL_TYPE_TIME:
118 return Poco::SQL::MetaColumn::FDT_TIME;
119
120 case MYSQL_TYPE_DATETIME:
121 return Poco::SQL::MetaColumn::FDT_TIMESTAMP;
122
123 case MYSQL_TYPE_STRING:
124 case MYSQL_TYPE_VAR_STRING:
125 return Poco::SQL::MetaColumn::FDT_STRING;
126
127 case MYSQL_TYPE_TINY_BLOB:
128 case MYSQL_TYPE_MEDIUM_BLOB:
129 case MYSQL_TYPE_LONG_BLOB:
130 case MYSQL_TYPE_BLOB:
131 return Poco::SQL::MetaColumn::FDT_BLOB;
132 default:
133 return Poco::SQL::MetaColumn::FDT_UNKNOWN;
134 }
135 }
136} // namespace
137
138
139namespace Poco {
140namespace SQL {
141namespace MySQL {
142
143void ResultMetadata::reset()
144{
145 _columns.resize(0);
146 _row.resize(0);
147 _buffer.resize(0);
148 _lengths.resize(0);
149 _isNull.resize(0);
150}
151
152void ResultMetadata::init(MYSQL_STMT* stmt)
153{
154 ResultMetadataHandle h(stmt);
155
156 if (!h)
157 {
158 // all right, it is normal
159 // querys such an "INSERT INTO" just does not have result at all
160 reset();
161 return;
162 }
163
164 std::size_t count = mysql_num_fields(h);
165 MYSQL_FIELD* fields = mysql_fetch_fields(h);
166
167 std::size_t commonSize = 0;
168 _columns.reserve(count);
169
170 {for (std::size_t i = 0; i < count; i++)
171 {
172 std::size_t size = fieldSize(fields[i]);
173 if (size == 0xFFFFFFFF) size = 0;
174
175 _columns.push_back(MetaColumn(
176 i, // position
177 fields[i].name, // name
178 fieldType(fields[i]), // type
179 size, // length
180 0, // TODO: precision
181 !IS_NOT_NULL(fields[i].flags) // nullable
182 ));
183
184 commonSize += _columns[i].length();
185 }}
186
187 _buffer.resize(commonSize);
188 _row.resize(count);
189 _lengths.resize(count);
190 _isNull.resize(count);
191
192 std::size_t offset = 0;
193
194 for (std::size_t i = 0; i < count; i++)
195 {
196 std::memset(&_row[i], 0, sizeof(MYSQL_BIND));
197 unsigned int len = static_cast<unsigned int>(_columns[i].length());
198 _row[i].buffer_type = fields[i].type;
199 _row[i].buffer_length = len;
200 _row[i].buffer = (len > 0) ? (&_buffer[0] + offset) : 0;
201 _row[i].length = &_lengths[i];
202 _row[i].is_null = &_isNull[i];
203 _row[i].is_unsigned = (fields[i].flags & UNSIGNED_FLAG) > 0;
204
205 offset += _row[i].buffer_length;
206 }
207}
208
209std::size_t ResultMetadata::columnsReturned() const
210{
211 return static_cast<std::size_t>(_columns.size());
212}
213
214const MetaColumn& ResultMetadata::metaColumn(std::size_t pos) const
215{
216 return _columns[pos];
217}
218
219MYSQL_BIND* ResultMetadata::row()
220{
221 return &_row[0];
222}
223
224std::size_t ResultMetadata::length(std::size_t pos) const
225{
226 return _lengths[pos];
227}
228
229const unsigned char* ResultMetadata::rawData(std::size_t pos) const
230{
231 return reinterpret_cast<const unsigned char*>(_row[pos].buffer);
232}
233
234bool ResultMetadata::isNull(std::size_t pos) const
235{
236 return (_isNull[pos] != 0);
237}
238
239}}} // namespace Poco::SQL::MySQL
240