1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtSql module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ** Oracle Spatial support: (C) 2012-2013 Juergen E. Fischer < jef at norbit dot de >, norBIT GmbH
41 **
42 ****************************************************************************/
43 // #define QOCISPATIAL_DEBUG
44 #ifndef QOCISPATIAL_DEBUG
45 #define QT_NO_DEBUG_OUTPUT
46 #endif
47
48 #if __cplusplus >= 201500
49 #define FALLTHROUGH [[fallthrough]];
50 #elif defined(__clang__)
51 #define FALLTHROUGH [[clang::fallthrough]];
52 #elif defined(__GNUC__) && __GNUC__ >= 7
53 #define FALLTHROUGH [[gnu::fallthrough]];
54 #else
55 #define FALLTHROUGH
56 #endif
57
58 #include "qsql_ocispatial.h"
59 #include "wkbptr.h"
60
61 #include <qcoreapplication.h>
62 #include <qvariant.h>
63 #include <qdatetime.h>
64 #include <qmetatype.h>
65 #include <qregexp.h>
66 #include <qshareddata.h>
67 #include <qsqlerror.h>
68 #include <qsqlfield.h>
69 #include <qsqlindex.h>
70 #include <qsqlquery.h>
71 #include <QtSql/private/qsqlcachedresult_p.h>
72 #include <QtSql/private/qsqldriver_p.h>
73 #include <qstringlist.h>
74 #include <qvarlengtharray.h>
75 #include <qvector.h>
76 #include <qdebug.h>
77 #include <cmath>
78
79 #ifdef Q_OS_WIN
80 #include <winsock.h>
81 #else
82 #include <netinet/in.h>
83 #endif
84
85 // This is needed for oracle oci when compiling with mingw-w64 headers
86 #if defined(__MINGW64_VERSION_MAJOR) && defined(_WIN64)
87 #define _int64 __int64
88 #endif
89
90
91 #include <oci.h>
92 #ifdef max
93 #undef max
94 #endif
95 #ifdef min
96 #undef min
97 #endif
98
99 #include <cstdlib>
100
101 #define QOCISPATIAL_DYNAMIC_CHUNK_SIZE 65535
102 #define QOCISPATIAL_PREFETCH_ROWS 10000
103 #define QOCISPATIAL_PREFETCH_MEM 8388608 // 8MB
104
105 // setting this define will allow using a query from a different
106 // thread than its database connection.
107 // warning - this is not fully tested and can lead to race conditions
108 #define QOCISPATIAL_THREADED
109
110
111 Q_DECLARE_OPAQUE_POINTER( OCIEnv * )
112 Q_DECLARE_OPAQUE_POINTER( OCIStmt * )
113 Q_DECLARE_METATYPE( OCIEnv * )
114 Q_DECLARE_METATYPE( OCIStmt * )
115
116 QT_BEGIN_NAMESPACE
117
118 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
119 enum { QOCISpatialEncoding = 2002 }; // AL16UTF16LE
120 #else
121 enum { QOCISpatialEncoding = 2000 }; // AL16UTF16
122 #endif
123
124 #ifdef OCI_ATTR_CHARSET_FORM
125 // Always set the OCI_ATTR_CHARSET_FORM to SQLCS_NCHAR is safe
126 // because Oracle server will deal with the implicit Conversion
127 // Between CHAR and NCHAR.
128 // see: http://download.oracle.com/docs/cd/A91202_01/901_doc/appdev.901/a89857/oci05bnd.htm#422705
129 static const ub1 qOraCharsetForm = SQLCS_NCHAR;
130 #endif
131
132 #if defined (OCI_UTF16ID)
133 static const ub2 qOraCharset = OCI_UTF16ID;
134 #else
135 static const ub2 qOraCharset = OCI_UCS2ID;
136 #endif
137
138 typedef QVarLengthArray<sb2, 32> IndicatorArray;
139 typedef QVarLengthArray<ub2, 32> SizeArray;
140
141 static QByteArray qMakeOraDate( const QDateTime &dt );
142 static QDateTime qMakeDate( const char *oraDate );
143
144 static QByteArray qMakeOCINumber( const qlonglong &ll, OCIError *err );
145 static QByteArray qMakeOCINumber( const qulonglong &ull, OCIError *err );
146
147 static qlonglong qMakeLongLong( const char *ociNumber, OCIError *err );
148 static qulonglong qMakeULongLong( const char *ociNumber, OCIError *err );
149
150 static QString qOraWarn( OCIError *err, int *errorCode = nullptr );
151
152 #ifndef Q_CC_SUN
153 static // for some reason, Sun CC can't use qOraWarning when it's declared static
154 #endif
155 void qOraWarningAt( const char *msg, OCIError *err, const char *function, const char *file, int line );
156 static QSqlError qMakeError( const QString &errString, QSqlError::ErrorType type, OCIError *err );
157
158 #ifndef _MSC_VER
159 #define qOraWarning(msg,err) qOraWarningAt(msg,err,__PRETTY_FUNCTION__,__FILE__,__LINE__)
160 #define OCI_VERIFY(x) do { oci_verify(__PRETTY_FUNCTION__, __FILE__, __LINE__, x, #x); } while(0)
161 #define OCI_VERIFY_E(e,x) do { oci_verify(e,__PRETTY_FUNCTION__, __FILE__, __LINE__, x, #x); } while(0)
162 #else
163 #define qOraWarning(msg,err) qOraWarningAt(msg,err,__FUNCTION__,__FILE__,__LINE__)
164 #define OCI_VERIFY(x) do { oci_verify(__FUNCTION__, __FILE__, __LINE__, x, #x); } while(0)
165 #define OCI_VERIFY_E(e,x) do { oci_verify(e,__FUNCTION__, __FILE__, __LINE__, x, #x); } while(0)
166 #endif
167
oci_verify(OCIError * err,const char * function,const char * file,int line,int result,const char * expression)168 void oci_verify( OCIError *err, const char *function, const char *file, int line, int result, const char *expression )
169 {
170 if ( result == OCI_SUCCESS || result == OCI_SUCCESS_WITH_INFO )
171 return;
172
173 qWarning( "%s:%d (%s) OCI error %s = %d [%s]", file, line, function, expression, result, qPrintable( qOraWarn( err ) ) );
174 throw result;
175 }
176
oci_verify(const char * function,const char * file,int line,int result,const char * expression)177 void oci_verify( const char *function, const char *file, int line, int result, const char *expression )
178 {
179 if ( result == OCI_SUCCESS || result == OCI_SUCCESS_WITH_INFO )
180 return;
181
182 qWarning( "%s:%d (%s) OCI error %s = %d", file, line, function, expression, result );
183 throw result;
184 }
185
186 #ifdef QOCISPATIAL_DEBUG
187 class enter
188 {
189 const char *mFunction = nullptr;
190 const char *mFile = nullptr;
191 int mLine;
192 static int level;
193
194 public:
enter(const char * function,const char * file,int line)195 enter( const char *function, const char *file, int line )
196 : mFunction( function ), mFile( file ), mLine( line )
197 {
198 qDebug( "+%*sEntering %s at %s:%d", level, "", mFunction, mFile, mLine );
199 level++;
200 }
201
~enter()202 ~enter()
203 {
204 level--;
205 qDebug( "-%*sLeaving %s", level, "", mFunction );
206 }
207 };
208
209 #ifdef _MSC_VER
210 #define ENTER enter here(__FUNCTION__,__FILE__,__LINE__);
211 #else
212 #define ENTER enter here(__PRETTY_FUNCTION__,__FILE__,__LINE__);
213 #endif
214
215 int enter::level = 0;
216 #else
217 #define ENTER
218 #endif
219
220 enum WKBType
221 {
222 WKBUnknown = 0,
223 WKBPoint = 1,
224 WKBLineString,
225 WKBPolygon,
226 WKBMultiPoint,
227 WKBMultiLineString,
228 WKBMultiPolygon,
229
230 WKBCircularString = 8,
231 WKBCompoundCurve = 9,
232 WKBCurvePolygon = 10,
233 WKBMultiCurve = 11,
234 WKBMultiSurface = 12,
235
236 WKBNoGeometry = 100, //attributes only
237 WKBCircularStringZ = 1008,
238 WKBCompoundCurveZ = 1009,
239 WKBCurvePolygonZ = 1010,
240 WKBMultiCurveZ = 1011,
241 WKBMultiSurfaceZ = 1012,
242
243 WKBPoint25D = 0x80000001,
244 WKBLineString25D,
245 WKBPolygon25D,
246 WKBMultiPoint25D,
247 WKBMultiLineString25D,
248 WKBMultiPolygon25D,
249
250 #if 0
251 WKBGeometry = 0,
252 WKBPoint = 1,
253 WKBLineString = 2,
254 WKBPolygon = 3,
255 WKBMultiPoint = 4,
256 WKBMultiLineString = 5,
257 WKBMultiPolygon = 6,
258 WKBGeometryCollection = 7,
259 WKBPolyhedralSurface = 15,
260 WKBTIN = 16,
261 WKBTriangle = 17,
262
263 WKBGeometryZ = 1000,
264 WKBPointZ = 1001,
265 WKBLineStringZ = 1002,
266 WKBPolygonZ = 1003,
267 WKBMultiPointZ = 1004,
268 WKBMultiLineStringZ = 1005,
269 WKBMultiPolygonZ = 1006,
270 WKBGeometryCollectionZ = 1007,
271 WKBPolyhedralSurfaceZ = 1015,
272 WKBTINZ = 1016,
273 WKBTriangleZ = 1017,
274
275 WKBGeometryM = 2000,
276 WKBPointM = 2001,
277 WKBLineStringM = 2002,
278 WKBPolygonM = 2003,
279 WKBMultiPointM = 2004,
280 WKBMultiLineStringM = 2005,
281 WKBMultiPolygonM = 2006,
282 WKBGeometryCollectionM = 2007,
283 WKBPolyhedralSurfaceM = 2015,
284 WKBTINM = 2016,
285 WKBTriangleM = 2017,
286
287 WKBGeometryZM = 3000,
288 WKBPointZM = 3001,
289 WKBLineStringZM = 3002,
290 WKBPolygonZM = 3003,
291 WKBMultiPointZM = 3004,
292 WKBMultiLineStringZM = 3005,
293 WKBMultiPolygonZM = 3006,
294 WKBGeometryCollectionZM = 3007,
295 WKBPolyhedralSurfaceZM = 3015,
296 WKBTINZM = 3016,
297 WKBTriangleZM = 3017,
298 #endif
299 };
300
301
302 class QOCISpatialRowId: public QSharedData
303 {
304 public:
305 explicit QOCISpatialRowId( OCIEnv *env );
306 ~QOCISpatialRowId();
307
308 OCIRowid *id = nullptr;
309
310 private:
QOCISpatialRowId(const QOCISpatialRowId & other)311 QOCISpatialRowId( const QOCISpatialRowId &other ): QSharedData( other ) { Q_ASSERT( false ); }
312 QOCISpatialRowId &operator= ( const QOCISpatialRowId & ) = delete;
313 };
314
QOCISpatialRowId(OCIEnv * env)315 QOCISpatialRowId::QOCISpatialRowId( OCIEnv *env )
316 {
317 OCIDescriptorAlloc( env, reinterpret_cast<dvoid **>( &id ),
318 OCI_DTYPE_ROWID, 0, nullptr );
319 }
320
~QOCISpatialRowId()321 QOCISpatialRowId::~QOCISpatialRowId()
322 {
323 if ( id )
324 OCIDescriptorFree( id, OCI_DTYPE_ROWID );
325 }
326
327 typedef QSharedDataPointer<QOCISpatialRowId> QOCISpatialRowIdPointer;
328 QT_BEGIN_INCLUDE_NAMESPACE
329 Q_DECLARE_METATYPE( QOCISpatialRowIdPointer )
330 QT_END_INCLUDE_NAMESPACE
331
332 class QOCISpatialDriverPrivate : public QSqlDriverPrivate
333 {
334 Q_DECLARE_PUBLIC( QOCISpatialDriver )
335
336 public:
337 QOCISpatialDriverPrivate();
338
339 OCIEnv *env = nullptr;
340 OCISvcCtx *svc = nullptr;
341 OCIServer *srvhp = nullptr;
342 OCISession *authp = nullptr;
343 OCIError *err = nullptr;
344 bool transaction = false;
345 int serverVersion = -1;
346 ub4 prefetchRows = QOCISPATIAL_PREFETCH_ROWS;
347 ub4 prefetchMem = QOCISPATIAL_PREFETCH_MEM;
348 bool commitOnSuccess = true;
349 QString user;
350
351 OCIType *geometryTDO = nullptr;
352
353 void allocErrorHandle();
354 OCIType *tdo( QString type );
355 };
356
357 class QOCISpatialCols;
358 class QOCISpatialResultPrivate;
359
360 class QOCISpatialResult: public QSqlCachedResult
361 {
362 Q_DECLARE_PRIVATE( QOCISpatialResult )
363 friend class QOCISpatialDriver;
364 friend class QOCISpatialCols;
365 public:
366 QOCISpatialResult( const QOCISpatialDriver *db );
367 ~QOCISpatialResult() override;
368 bool prepare( const QString &query ) override;
369 bool exec() override;
370 QVariant handle() const override;
371
372 protected:
373 bool gotoNext( ValueCache &values, int index ) override;
374 bool reset( const QString &query ) override;
375 int size() override;
376 int numRowsAffected() override;
377 QSqlRecord record() const override;
378 QVariant lastInsertId() const override;
379 bool execBatch( bool arrayBind = false ) override;
380 void virtual_hook( int id, void *data ) override;
381 };
382
383 struct QOCISDOPointObj
384 {
385 OCINumber x;
386 OCINumber y;
387 OCINumber z;
388 };
389
390 struct QOCISDOGeometryObj
391 {
392 OCINumber gtype;
393 OCINumber srid;
394 QOCISDOPointObj point;
395 OCIArray *elem_info = nullptr;
396 OCIArray *ordinates = nullptr;
397 };
398
399 struct QOCISDOPointInd
400 {
401 OCIInd _atomic;
402 OCIInd x;
403 OCIInd y;
404 OCIInd z;
405 };
406
407 struct QOCISDOGeometryInd
408 {
409 OCIInd _atomic;
410 OCIInd gtype;
411 OCIInd srid;
412 QOCISDOPointInd point;
413 OCIInd elem_info;
414 OCIInd ordinates;
415 };
416
417
418 class QOCISpatialResultPrivate: public QSqlCachedResultPrivate
419 {
420 public:
421 Q_DECLARE_PUBLIC( QOCISpatialResult )
422 Q_DECLARE_SQLDRIVER_PRIVATE( QOCISpatialDriver )
423 QOCISpatialResultPrivate( QOCISpatialResult *q, const QOCISpatialDriver *drv );
424 ~QOCISpatialResultPrivate() override;
425
426 QOCISpatialCols *cols = nullptr;
427 OCIEnv *env = nullptr;
428 OCIError *err = nullptr;
429 OCISvcCtx *&svc;
430 OCIStmt *sql = nullptr;
431 QList<QOCISDOGeometryObj *> sdoobj;
432 QList<QOCISDOGeometryInd *> sdoind;
433 bool transaction;
434 bool commitOnSuccess = true;
435 int serverVersion;
436 ub4 prefetchRows, prefetchMem;
437 OCIType *geometryTDO = nullptr;
438 QOCISDOGeometryObj *geometryObj = nullptr;
439 QOCISDOGeometryInd *geometryInd = nullptr;
440
441 void setStatementAttributes();
442 int bindValue( OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
443 const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage );
444 int bindValues( QVector<QVariant> &values, IndicatorArray &indicators, SizeArray &tmpSizes,
445 QList<QByteArray> &tmpStorage );
446 void outValues( QVector<QVariant> &values, IndicatorArray &indicators,
447 QList<QByteArray> &tmpStorage );
isOutValue(int i) const448 inline bool isOutValue( int i ) const
449 { Q_Q( const QOCISpatialResult ); return q->bindValueType( i ) & QSql::Out; }
isBinaryValue(int i) const450 inline bool isBinaryValue( int i ) const
451 { Q_Q( const QOCISpatialResult ); return q->bindValueType( i ) & QSql::Binary; }
452
setCharset(dvoid * handle,ub4 type) const453 void setCharset( dvoid *handle, ub4 type ) const
454 {
455 int r = OCI_SUCCESS;
456 Q_ASSERT( handle );
457
458 #ifdef OCI_ATTR_CHARSET_FORM
459 r = OCIAttrSet( handle,
460 type,
461 // this const cast is safe since OCI doesn't touch
462 // the charset.
463 const_cast<void *>( static_cast<const void *>( &qOraCharsetForm ) ),
464 0,
465 OCI_ATTR_CHARSET_FORM,
466 //Strange Oracle bug: some Oracle servers crash the server process with non-zero error handle (mostly for 10g).
467 //So ignore the error message here.
468 nullptr );
469 #ifdef QOCISPATIAL_DEBUG
470 if ( r != OCI_SUCCESS )
471 qWarning( "QOCISpatialResultPrivate::setCharset: Couldn't set OCI_ATTR_CHARSET_FORM." );
472 #endif
473 #endif
474
475 r = OCIAttrSet( handle,
476 type,
477 // this const cast is safe since OCI doesn't touch
478 // the charset.
479 const_cast<void *>( static_cast<const void *>( &qOraCharset ) ),
480 0,
481 OCI_ATTR_CHARSET_ID,
482 err );
483 if ( r != OCI_SUCCESS )
484 qOraWarning( "Couldn't set OCI_ATTR_CHARSET_ID: ", err );
485
486 }
487 };
488
setStatementAttributes()489 void QOCISpatialResultPrivate::setStatementAttributes()
490 {
491 ENTER
492 Q_ASSERT( sql );
493
494 int r = OCI_SUCCESS;
495
496 r = OCIAttrSet( sql,
497 OCI_HTYPE_STMT,
498 &prefetchRows,
499 0,
500 OCI_ATTR_PREFETCH_ROWS,
501 err );
502 if ( r != OCI_SUCCESS )
503 qOraWarning( "Couldn't set OCI_ATTR_PREFETCH_ROWS: ", err );
504 r = OCIAttrSet( sql,
505 OCI_HTYPE_STMT,
506 &prefetchMem,
507 0,
508 OCI_ATTR_PREFETCH_MEMORY,
509 err );
510 if ( r != OCI_SUCCESS )
511 qOraWarning( "QOCISpatialResultPrivate::setStatementAttributes:"
512 " Couldn't set OCI_ATTR_PREFETCH_MEMORY: ", err );
513 }
514
bindValue(OCIStmt * sql,OCIBind ** hbnd,OCIError * err,int pos,const QVariant & val,dvoid * indPtr,ub2 * tmpSize,QList<QByteArray> & tmpStorage)515 int QOCISpatialResultPrivate::bindValue( OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
516 const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage )
517 {
518 ENTER
519 int r = OCI_SUCCESS;
520 void *data = const_cast<void *>( val.constData() );
521
522 switch ( val.type() )
523 {
524 case QVariant::ByteArray:
525 r = OCIBindByPos( sql, hbnd, err,
526 pos + 1,
527 isOutValue( pos )
528 ? const_cast<char *>( reinterpret_cast<QByteArray *>( data )->constData() )
529 : reinterpret_cast<QByteArray *>( data )->data(),
530 reinterpret_cast<QByteArray *>( data )->size(),
531 SQLT_BIN, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
532 qDebug() << "inout" << isOutValue( pos ) << "bytearray size" << reinterpret_cast<QByteArray *>( data )->size() << "r" << r;
533 break;
534 case QVariant::Time:
535 case QVariant::Date:
536 case QVariant::DateTime:
537 {
538 QByteArray ba = qMakeOraDate( val.toDateTime() );
539 r = OCIBindByPos( sql, hbnd, err,
540 pos + 1,
541 ba.data(),
542 ba.size(),
543 SQLT_DAT, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
544 tmpStorage.append( ba );
545 break;
546 }
547 case QVariant::Int:
548 r = OCIBindByPos( sql, hbnd, err,
549 pos + 1,
550 // if it's an out value, the data is already detached
551 // so the const cast is safe.
552 const_cast<void *>( data ),
553 sizeof( int ),
554 SQLT_INT, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
555 break;
556 case QVariant::UInt:
557 r = OCIBindByPos( sql, hbnd, err,
558 pos + 1,
559 // if it's an out value, the data is already detached
560 // so the const cast is safe.
561 const_cast<void *>( data ),
562 sizeof( uint ),
563 SQLT_UIN, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
564 break;
565 case QVariant::LongLong:
566 {
567 QByteArray ba = qMakeOCINumber( val.toLongLong(), err );
568 r = OCIBindByPos( sql, hbnd, err,
569 pos + 1,
570 ba.data(),
571 ba.size(),
572 SQLT_VNU, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
573 tmpStorage.append( ba );
574 break;
575 }
576 case QVariant::ULongLong:
577 {
578 QByteArray ba = qMakeOCINumber( val.toULongLong(), err );
579 r = OCIBindByPos( sql, hbnd, err,
580 pos + 1,
581 ba.data(),
582 ba.size(),
583 SQLT_VNU, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
584 tmpStorage.append( ba );
585 break;
586 }
587 case QVariant::Double:
588 r = OCIBindByPos( sql, hbnd, err,
589 pos + 1,
590 // if it's an out value, the data is already detached
591 // so the const cast is safe.
592 const_cast<void *>( data ),
593 sizeof( double ),
594 SQLT_FLT, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
595 break;
596 case QVariant::UserType:
597 if ( val.canConvert<QOCISpatialGeometry>() && !isOutValue( pos ) )
598 {
599 try
600 {
601 if ( !geometryObj )
602 {
603 OCI_VERIFY_E( err, OCIObjectNew( env, err, svc, OCI_TYPECODE_OBJECT, geometryTDO, ( dvoid * ) nullptr, OCI_DURATION_SESSION, 1, ( dvoid ** ) &geometryObj ) );
604 if ( !geometryObj )
605 {
606 throw OCI_ERROR;
607 }
608
609 OCI_VERIFY_E( err, OCIObjectGetInd( env, err, geometryObj, ( void ** ) &geometryInd ) );
610 if ( !geometryInd )
611 {
612 throw OCI_ERROR;
613 }
614 }
615
616 OCI_VERIFY_E( err, OCIBindByPos( sql, hbnd, err, pos + 1, nullptr, 0, SQLT_NTY, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT ) );
617 OCI_VERIFY_E( err, OCIBindObject( *hbnd, err, geometryTDO, ( dvoid ** )&geometryObj, nullptr, ( dvoid ** ) &geometryInd, nullptr ) );
618
619 const QOCISpatialGeometry &g = qvariant_cast<QOCISpatialGeometry>( val );
620
621 int n;
622 OCI_VERIFY_E( err, OCICollSize( env, err, geometryObj->elem_info, &n ) );
623 OCI_VERIFY_E( err, OCICollTrim( env, err, n, geometryObj->elem_info ) );
624
625 OCI_VERIFY_E( err, OCICollSize( env, err, geometryObj->ordinates, &n ) );
626 OCI_VERIFY_E( err, OCICollTrim( env, err, n, geometryObj->ordinates ) );
627
628 if ( g.isNull )
629 {
630 geometryInd->_atomic = OCI_IND_NULL;
631 }
632 else
633 {
634 geometryInd->_atomic = OCI_IND_NOTNULL;
635 geometryInd->gtype = g.gtype < 0 ? OCI_IND_NULL : OCI_IND_NOTNULL;
636 geometryInd->srid = g.srid < 0 ? OCI_IND_NULL : OCI_IND_NOTNULL;
637
638 OCI_VERIFY_E( err, OCINumberFromInt( err, &g.gtype, sizeof( int ), OCI_NUMBER_SIGNED, &geometryObj->gtype ) );
639 OCI_VERIFY_E( err, OCINumberFromInt( err, &g.srid, sizeof( int ), OCI_NUMBER_SIGNED, &geometryObj->srid ) );
640
641 if ( SDO_GTYPE_TT( g.gtype ) == GtPoint )
642 {
643 geometryInd->point._atomic = OCI_IND_NOTNULL;
644 geometryInd->point.x = OCI_IND_NOTNULL;
645 geometryInd->point.y = OCI_IND_NOTNULL;
646 geometryInd->point.z = OCI_IND_NOTNULL;
647 geometryInd->elem_info = OCI_IND_NULL;
648 geometryInd->ordinates = OCI_IND_NULL;
649
650 OCI_VERIFY_E( err, OCINumberFromReal( err, &g.x, sizeof( double ), &geometryObj->point.x ) );
651 OCI_VERIFY_E( err, OCINumberFromReal( err, &g.y, sizeof( double ), &geometryObj->point.y ) );
652 OCI_VERIFY_E( err, OCINumberFromReal( err, &g.z, sizeof( double ), &geometryObj->point.z ) );
653 }
654 else
655 {
656 geometryInd->point._atomic = OCI_IND_NULL;
657 geometryInd->elem_info = g.eleminfo.size() == 0 ? OCI_IND_NULL : OCI_IND_NOTNULL;
658 geometryInd->ordinates = g.ordinates.size() == 0 ? OCI_IND_NULL : OCI_IND_NOTNULL;
659
660 foreach ( int e, g.eleminfo )
661 {
662 OCINumber n;
663 OCI_VERIFY_E( err, OCINumberFromInt( err, &e, sizeof( int ), OCI_NUMBER_SIGNED, &n ) );
664 OCI_VERIFY_E( err, OCICollAppend( env, err, &n, nullptr, geometryObj->elem_info ) );
665 }
666
667 foreach ( double o, g.ordinates )
668 {
669 OCINumber n;
670 OCI_VERIFY_E( err, OCINumberFromReal( err, &o, sizeof( double ), &n ) );
671 OCI_VERIFY_E( err, OCICollAppend( env, err, &n, nullptr, geometryObj->ordinates ) );
672 }
673 }
674 }
675 }
676 catch ( int e )
677 {
678 r = e;
679 }
680 }
681 else if ( val.canConvert<QOCISpatialRowIdPointer>() && !isOutValue( pos ) )
682 {
683 // use a const pointer to prevent a detach
684 const QOCISpatialRowIdPointer rptr = qvariant_cast<QOCISpatialRowIdPointer>( val );
685 r = OCIBindByPos( sql, hbnd, err,
686 pos + 1,
687 // it's an IN value, so const_cast is OK
688 const_cast<OCIRowid **>( &rptr->id ),
689 -1,
690 SQLT_RDD, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
691 }
692 else
693 {
694 qWarning( "Unknown bind variable" );
695 r = OCI_ERROR;
696 }
697 break;
698 case QVariant::String:
699 {
700 const QString s = val.toString();
701 if ( isBinaryValue( pos ) )
702 {
703 r = OCIBindByPos( sql, hbnd, err,
704 pos + 1,
705 const_cast<ushort *>( s.utf16() ),
706 s.length() * sizeof( QChar ),
707 SQLT_LNG, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
708 break;
709 }
710 else if ( !isOutValue( pos ) )
711 {
712 // don't detach the string
713 r = OCIBindByPos( sql, hbnd, err,
714 pos + 1,
715 // safe since oracle doesn't touch OUT values
716 const_cast<ushort *>( s.utf16() ),
717 ( s.length() + 1 ) * sizeof( QChar ),
718 SQLT_STR, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
719 if ( r == OCI_SUCCESS )
720 setCharset( *hbnd, OCI_HTYPE_BIND );
721 break;
722 }
723 }
724 FALLTHROUGH
725
726 default:
727 {
728 const QString s = val.toString();
729 // create a deep-copy
730 QByteArray ba( reinterpret_cast<const char *>( s.utf16() ), ( s.length() + 1 ) * sizeof( QChar ) );
731 if ( isOutValue( pos ) )
732 {
733 ba.reserve( ( s.capacity() + 1 ) * sizeof( QChar ) );
734 *tmpSize = ba.size();
735 r = OCIBindByPos( sql, hbnd, err,
736 pos + 1,
737 ba.data(),
738 ba.capacity(),
739 SQLT_STR, indPtr, tmpSize, nullptr, 0, nullptr, OCI_DEFAULT );
740 tmpStorage.append( ba );
741 }
742 else
743 {
744 r = OCIBindByPos( sql, hbnd, err,
745 pos + 1,
746 ba.data(),
747 ba.size(),
748 SQLT_STR, indPtr, nullptr, nullptr, 0, nullptr, OCI_DEFAULT );
749 }
750 if ( r == OCI_SUCCESS )
751 setCharset( *hbnd, OCI_HTYPE_BIND );
752 break;
753 } // default case
754 } // switch
755 if ( r != OCI_SUCCESS )
756 qOraWarning( "QOCISpatialResultPrivate::bindValue:", err );
757 return r;
758 }
759
bindValues(QVector<QVariant> & values,IndicatorArray & indicators,SizeArray & tmpSizes,QList<QByteArray> & tmpStorage)760 int QOCISpatialResultPrivate::bindValues( QVector<QVariant> &values, IndicatorArray &indicators,
761 SizeArray &tmpSizes, QList<QByteArray> &tmpStorage )
762 {
763 ENTER
764 int r = OCI_SUCCESS;
765 for ( int i = 0; i < values.count(); ++i )
766 {
767 if ( isOutValue( i ) )
768 values[i].detach();
769 const QVariant &val = values.at( i );
770
771 OCIBind *hbnd = nullptr; // Oracle handles these automatically
772 sb2 *indPtr = &indicators[i];
773 *indPtr = val.isNull() ? -1 : 0;
774
775 bindValue( sql, &hbnd, err, i, val, indPtr, &tmpSizes[i], tmpStorage );
776 }
777 return r;
778 }
779
780 // will assign out value and remove its temp storage.
qOraOutValue(QVariant & value,QList<QByteArray> & storage,OCIError * err)781 static void qOraOutValue( QVariant &value, QList<QByteArray> &storage, OCIError *err )
782 {
783 ENTER
784 switch ( value.type() )
785 {
786 case QVariant::Time:
787 value = qMakeDate( storage.takeFirst() ).time();
788 break;
789 case QVariant::Date:
790 value = qMakeDate( storage.takeFirst() ).date();
791 break;
792 case QVariant::DateTime:
793 value = qMakeDate( storage.takeFirst() );
794 break;
795 case QVariant::LongLong:
796 value = qMakeLongLong( storage.takeFirst(), err );
797 break;
798 case QVariant::ULongLong:
799 value = qMakeULongLong( storage.takeFirst(), err );
800 break;
801 case QVariant::String:
802 value = QString(
803 reinterpret_cast<const QChar *>( storage.takeFirst().constData() ) );
804 break;
805 default:
806 break; //nothing
807 }
808 }
809
outValues(QVector<QVariant> & values,IndicatorArray & indicators,QList<QByteArray> & tmpStorage)810 void QOCISpatialResultPrivate::outValues( QVector<QVariant> &values, IndicatorArray &indicators,
811 QList<QByteArray> &tmpStorage )
812 {
813 ENTER
814 for ( int i = 0; i < values.count(); ++i )
815 {
816
817 if ( !isOutValue( i ) )
818 continue;
819
820 qOraOutValue( values[i], tmpStorage, err );
821
822 QVariant::Type typ = values.at( i ).type();
823 if ( indicators[i] == -1 ) // NULL
824 values[i] = QVariant( typ );
825 else
826 values[i] = QVariant( typ, values.at( i ).constData() );
827 }
828 }
829
830
QOCISpatialDriverPrivate()831 QOCISpatialDriverPrivate::QOCISpatialDriverPrivate()
832 {
833 ENTER
834 }
835
allocErrorHandle()836 void QOCISpatialDriverPrivate::allocErrorHandle()
837 {
838 ENTER
839 int r = OCIHandleAlloc( env,
840 reinterpret_cast<void **>( &err ),
841 OCI_HTYPE_ERROR,
842 0,
843 nullptr );
844 if ( r != OCI_SUCCESS )
845 qWarning( "QOCISpatialDriver: unable to allocate error handle" );
846 }
847
tdo(QString type)848 OCIType *QOCISpatialDriverPrivate::tdo( QString type )
849 {
850 OCIParam *paramp = nullptr;
851 OCIRef *type_ref = nullptr;
852 OCIType *tdo = nullptr;
853 OCIDescribe *dschp = nullptr;
854
855 try
856 {
857 OCI_VERIFY( OCIHandleAlloc( env, ( void ** ) & dschp, OCI_HTYPE_DESCRIBE, 0, nullptr ) );
858 OCI_VERIFY_E( err, OCIDescribeAny( svc, err, ( dvoid * ) type.utf16(), type.length() * sizeof( QChar ), OCI_OTYPE_NAME, OCI_DEFAULT, OCI_PTYPE_TYPE, dschp ) );
859 OCI_VERIFY_E( err, OCIAttrGet( dschp, OCI_HTYPE_DESCRIBE, ¶mp, nullptr, OCI_ATTR_PARAM, err ) );
860 OCI_VERIFY_E( err, OCIAttrGet( paramp, OCI_DTYPE_PARAM, &type_ref, nullptr, OCI_ATTR_REF_TDO, err ) );
861 OCI_VERIFY_E( err, OCIObjectPin( env, err, type_ref, nullptr, OCI_PIN_ANY, OCI_DURATION_SESSION, OCI_LOCK_NONE, ( dvoid ** ) &tdo ) );
862 }
863 catch ( int r )
864 {
865 Q_UNUSED( r )
866 return nullptr;
867 }
868
869 return tdo;
870 }
871
872 struct OraFieldInfo
873 {
874 QString name;
875 QVariant::Type type;
876 ub1 oraIsNull;
877 ub4 oraType;
878 sb1 oraScale;
879 ub4 oraLength; // size in bytes
880 ub4 oraFieldLength; // amount of characters
881 sb2 oraPrecision;
882 QString oraTypeName;
883 OCIType *oraOCIType = nullptr;
884 };
885
qOraWarn(OCIError * err,int * errorCode)886 QString qOraWarn( OCIError *err, int *errorCode )
887 {
888 sb4 errcode;
889 text errbuf[1024];
890 errbuf[0] = 0;
891 errbuf[1] = 0;
892
893 OCIErrorGet( err,
894 1,
895 nullptr,
896 &errcode,
897 errbuf,
898 sizeof( errbuf ),
899 OCI_HTYPE_ERROR );
900 if ( errorCode )
901 *errorCode = errcode;
902 return QString( reinterpret_cast<const QChar *>( errbuf ) );
903 }
904
qOraWarningAt(const char * msg,OCIError * err,const char * function,const char * file,int line)905 void qOraWarningAt( const char *msg, OCIError *err, const char *function, const char *file, int line )
906 {
907 qWarning( "%s: %d: (%s) %s [%s]", file, line, function, msg, qPrintable( qOraWarn( err ) ) );
908 }
909
qOraErrorNumber(OCIError * err)910 static int qOraErrorNumber( OCIError *err )
911 {
912 ENTER
913 sb4 errcode;
914 OCIErrorGet( err,
915 1,
916 nullptr,
917 &errcode,
918 nullptr,
919 0,
920 OCI_HTYPE_ERROR );
921 return errcode;
922 }
923
qMakeError(const QString & errString,QSqlError::ErrorType type,OCIError * err)924 QSqlError qMakeError( const QString &errString, QSqlError::ErrorType type, OCIError *err )
925 {
926 ENTER
927 int errorCode = 0;
928 const QString oraErrorString = qOraWarn( err, &errorCode );
929 return QSqlError( errString, oraErrorString, type, QString::number( errorCode ) );
930 }
931
qDecodeOCIType(const QString & ocitype,QSql::NumericalPrecisionPolicy precisionPolicy)932 QVariant::Type qDecodeOCIType( const QString &ocitype, QSql::NumericalPrecisionPolicy precisionPolicy )
933 {
934 ENTER
935 QVariant::Type type = QVariant::Invalid;
936 qDebug( "qDecodeOCIType(ocitype=%s, precisionPolicy=%d)\n", ocitype.toLocal8Bit().constData(), precisionPolicy );
937 if ( ocitype == QLatin1String( "VARCHAR2" ) || ocitype == QLatin1String( "VARCHAR" )
938 || ocitype.startsWith( QLatin1String( "INTERVAL" ) )
939 || ocitype == QLatin1String( "CHAR" ) || ocitype == QLatin1String( "NVARCHAR2" )
940 || ocitype == QLatin1String( "NCHAR" ) )
941 type = QVariant::String;
942 else if ( ocitype == QLatin1String( "NUMBER" )
943 || ocitype == QLatin1String( "FLOAT" )
944 || ocitype == QLatin1String( "BINARY_FLOAT" )
945 || ocitype == QLatin1String( "BINARY_DOUBLE" ) )
946 {
947 switch ( precisionPolicy )
948 {
949 case QSql::LowPrecisionInt32:
950 type = QVariant::Int;
951 break;
952 case QSql::LowPrecisionInt64:
953 type = QVariant::LongLong;
954 break;
955 case QSql::LowPrecisionDouble:
956 type = QVariant::Double;
957 break;
958 case QSql::HighPrecision:
959 default:
960 type = QVariant::String;
961 break;
962 }
963 }
964 else if ( ocitype == QLatin1String( "LONG" ) || ocitype == QLatin1String( "NCLOB" )
965 || ocitype == QLatin1String( "CLOB" ) )
966 type = QVariant::ByteArray;
967 else if ( ocitype == QLatin1String( "RAW" ) || ocitype == QLatin1String( "LONG RAW" )
968 || ocitype == QLatin1String( "ROWID" ) || ocitype == QLatin1String( "BLOB" )
969 || ocitype == QLatin1String( "CFILE" ) || ocitype == QLatin1String( "BFILE" ) )
970 type = QVariant::ByteArray;
971 else if ( ocitype == QLatin1String( "DATE" ) || ocitype.startsWith( QLatin1String( "TIME" ) ) )
972 type = QVariant::DateTime;
973 else if ( ocitype == QLatin1String( "UNDEFINED" ) )
974 type = QVariant::Invalid;
975 if ( type == QVariant::Invalid )
976 qWarning( "qDecodeOCIType: unknown type: %s", ocitype.toLocal8Bit().constData() );
977 return type;
978 }
979
qDecodeOCIType(int ocitype,QSql::NumericalPrecisionPolicy precisionPolicy)980 QVariant::Type qDecodeOCIType( int ocitype, QSql::NumericalPrecisionPolicy precisionPolicy )
981 {
982 ENTER
983 QVariant::Type type = QVariant::Invalid;
984 qDebug( "qDecodeOCIType(ocitype=%d, precisionPolicy=%d)\n", ocitype, precisionPolicy );
985 switch ( ocitype )
986 {
987 case SQLT_STR:
988 case SQLT_VST:
989 case SQLT_CHR:
990 case SQLT_AFC:
991 case SQLT_VCS:
992 case SQLT_AVC:
993 case SQLT_RDD:
994 case SQLT_LNG:
995 #ifdef SQLT_INTERVAL_YM
996 case SQLT_INTERVAL_YM:
997 #endif
998 #ifdef SQLT_INTERVAL_DS
999 case SQLT_INTERVAL_DS:
1000 #endif
1001 type = QVariant::String;
1002 break;
1003 case SQLT_INT:
1004 type = QVariant::Int;
1005 break;
1006 case SQLT_FLT:
1007 case SQLT_NUM:
1008 case SQLT_VNU:
1009 case SQLT_UIN:
1010 case SQLT_IBDOUBLE:
1011 case SQLT_IBFLOAT:
1012 switch ( precisionPolicy )
1013 {
1014 case QSql::LowPrecisionInt32:
1015 type = QVariant::Int;
1016 break;
1017 case QSql::LowPrecisionInt64:
1018 type = QVariant::LongLong;
1019 break;
1020 case QSql::LowPrecisionDouble:
1021 type = QVariant::Double;
1022 break;
1023 case QSql::HighPrecision:
1024 default:
1025 type = QVariant::String;
1026 break;
1027 }
1028 break;
1029 case SQLT_VBI:
1030 case SQLT_BIN:
1031 case SQLT_LBI:
1032 case SQLT_LVC:
1033 case SQLT_LVB:
1034 case SQLT_BLOB:
1035 case SQLT_CLOB:
1036 case SQLT_FILE:
1037 case SQLT_NTY:
1038 case SQLT_REF:
1039 case SQLT_RID:
1040 type = QVariant::ByteArray;
1041 break;
1042 case SQLT_DAT:
1043 case SQLT_ODT:
1044 #ifdef SQLT_TIMESTAMP
1045 case SQLT_TIMESTAMP:
1046 case SQLT_TIMESTAMP_TZ:
1047 case SQLT_TIMESTAMP_LTZ:
1048 #endif
1049 type = QVariant::DateTime;
1050 break;
1051 default:
1052 type = QVariant::Invalid;
1053 qWarning( "qDecodeOCIType: unknown OCI datatype: %d", ocitype );
1054 break;
1055 }
1056 return type;
1057 }
1058
qFromOraInf(const OraFieldInfo & ofi)1059 static QSqlField qFromOraInf( const OraFieldInfo &ofi )
1060 {
1061 ENTER
1062 QSqlField f( ofi.name, ofi.type );
1063 f.setRequired( ofi.oraIsNull == 0 );
1064
1065 if ( ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU )
1066 f.setLength( ofi.oraFieldLength );
1067 else
1068 f.setLength( ofi.oraPrecision == 0 ? 38 : int( ofi.oraPrecision ) );
1069
1070 f.setPrecision( ofi.oraScale );
1071 f.setSqlType( int( ofi.oraType ) );
1072 return f;
1073 }
1074
1075 /*!
1076 \internal
1077
1078 Convert QDateTime to the internal Oracle DATE format NB!
1079 It does not handle BCE dates.
1080 */
qMakeOraDate(const QDateTime & dt)1081 QByteArray qMakeOraDate( const QDateTime &dt )
1082 {
1083 ENTER
1084 QByteArray ba;
1085 ba.resize( 7 );
1086 int year = dt.date().year();
1087 ba[0] = ( year / 100 ) + 100; // century
1088 ba[1] = ( year % 100 ) + 100; // year
1089 ba[2] = dt.date().month();
1090 ba[3] = dt.date().day();
1091 ba[4] = dt.time().hour() + 1;
1092 ba[5] = dt.time().minute() + 1;
1093 ba[6] = dt.time().second() + 1;
1094 return ba;
1095 }
1096
1097 /*!
1098 \internal
1099
1100 Convert qlonglong to the internal Oracle OCINumber format.
1101 */
qMakeOCINumber(const qlonglong & ll,OCIError * err)1102 QByteArray qMakeOCINumber( const qlonglong &ll, OCIError *err )
1103 {
1104 ENTER
1105 QByteArray ba( sizeof( OCINumber ), 0 );
1106
1107 OCINumberFromInt( err,
1108 &ll,
1109 sizeof( qlonglong ),
1110 OCI_NUMBER_SIGNED,
1111 reinterpret_cast<OCINumber *>( ba.data() ) );
1112 return ba;
1113 }
1114
1115 /*!
1116 \internal
1117
1118 Convert qulonglong to the internal Oracle OCINumber format.
1119 */
qMakeOCINumber(const qulonglong & ull,OCIError * err)1120 QByteArray qMakeOCINumber( const qulonglong &ull, OCIError *err )
1121 {
1122 ENTER
1123 QByteArray ba( sizeof( OCINumber ), 0 );
1124
1125 OCINumberFromInt( err,
1126 &ull,
1127 sizeof( qlonglong ),
1128 OCI_NUMBER_UNSIGNED,
1129 reinterpret_cast<OCINumber *>( ba.data() ) );
1130 return ba;
1131 }
1132
qMakeLongLong(const char * ociNumber,OCIError * err)1133 qlonglong qMakeLongLong( const char *ociNumber, OCIError *err )
1134 {
1135 ENTER
1136 qlonglong qll = 0;
1137 OCINumberToInt( err, reinterpret_cast<const OCINumber *>( ociNumber ), sizeof( qlonglong ),
1138 OCI_NUMBER_SIGNED, &qll );
1139 return qll;
1140 }
1141
qMakeULongLong(const char * ociNumber,OCIError * err)1142 qulonglong qMakeULongLong( const char *ociNumber, OCIError *err )
1143 {
1144 ENTER
1145 qulonglong qull = 0;
1146 OCINumberToInt( err, reinterpret_cast<const OCINumber *>( ociNumber ), sizeof( qulonglong ),
1147 OCI_NUMBER_UNSIGNED, &qull );
1148 return qull;
1149 }
1150
qMakeDate(const char * oraDate)1151 QDateTime qMakeDate( const char *oraDate )
1152 {
1153 ENTER
1154 int century = uchar( oraDate[0] );
1155 if ( century >= 100 )
1156 {
1157 int year = uchar( oraDate[1] );
1158 year = ( ( century - 100 ) * 100 ) + ( year - 100 );
1159 int month = oraDate[2];
1160 int day = oraDate[3];
1161 int hour = oraDate[4] - 1;
1162 int min = oraDate[5] - 1;
1163 int sec = oraDate[6] - 1;
1164 return QDateTime( QDate( year, month, day ), QTime( hour, min, sec ) );
1165 }
1166 return QDateTime();
1167 }
1168
1169 class QOCISpatialCols
1170 {
1171 public:
1172 QOCISpatialCols( int size, QOCISpatialResultPrivate *dp );
1173 ~QOCISpatialCols();
1174 int readPiecewise( QVector<QVariant> &values, int index = 0 );
1175 int readLOBs( QVector<QVariant> &values, int index = 0 );
1176 int fieldFromDefine( OCIDefine *d );
1177 void getValues( QVector<QVariant> &v, int index );
size()1178 inline int size() { return fieldInf.size(); }
1179 static bool execBatch( QOCISpatialResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind );
1180
1181 QSqlRecord rec;
1182
1183 struct Point
1184 {
PointQOCISpatialCols::Point1185 Point( double x = 0, double y = 0, double z = 0 )
1186 : x( x )
1187 , y( y )
1188 , z( z )
1189 {}
1190
1191 double x = 0;
1192 double y = 0;
1193 double z = 0;
1194 };
1195 typedef QVector< Point > PointSequence;
1196 typedef QPair< WKBType, PointSequence > CurvePart;
1197 typedef QVector< CurvePart > CurveParts;
1198 typedef QVector< QPair< WKBType, CurveParts > > SurfaceRings;
1199
1200 private:
1201 char *create( int position, int size );
1202 OCILobLocator **createLobLocator( int position, OCIEnv *env );
1203 OraFieldInfo qMakeOraField( const QOCISpatialResultPrivate *p, OCIParam *param ) const;
1204
1205 class OraFieldInf
1206 {
1207 public:
1208 OraFieldInf() = default;
1209 ~OraFieldInf();
1210 char *data = nullptr;
1211 int len = 0;
1212 sb2 ind = 0;
1213 QVariant::Type typ = QVariant::Invalid;
1214 ub4 oraType = 0;
1215 OCIDefine *def = nullptr;
1216 OCILobLocator *lob = nullptr;
1217 QString oraTypeName;
1218 };
1219
1220 bool convertToWkb( QVariant &v, int index );
1221
1222 PointSequence circlePoints( double x1, double y1, double x2, double y2, double x3, double y3 );
1223
1224 QOCISpatialCols::CurveParts getCurveParts( int &iElem, const QVector<int> &vElems, int nOrds, const QVector<double> &ordinates, int nDims, WKBType &baseType, bool &ok );
1225 bool getValue( OCINumber *num, unsigned int &value );
1226 bool getValue( OCINumber *num, int &value );
1227 bool getValue( OCINumber *num, double &value );
1228 bool getArraySize( OCIColl *coll, int &nSize );
1229 bool getElemInfoElem( int elem, const QVector<int> &vElem, int nOrds, int &startOffset, int &endOffset, int &etype, int &interpretation );
byteorder()1230 static int byteorder() { static char littleEndian = htonl( 1 ) != 1; return littleEndian; }
1231
1232 #ifdef QOCISPATIAL_DEBUG
1233 void dumpArrays( int nElems, int nOrds );
1234 #endif
1235
1236 QVector<OraFieldInf> fieldInf;
1237 const QOCISpatialResultPrivate *const d;
1238 };
1239
1240
1241 Q_DECLARE_TYPEINFO( QOCISpatialCols::Point, Q_PRIMITIVE_TYPE );
1242
1243
~OraFieldInf()1244 QOCISpatialCols::OraFieldInf::~OraFieldInf()
1245 {
1246 ENTER
1247 delete [] data;
1248 if ( lob )
1249 {
1250 int r = OCIDescriptorFree( lob, OCI_DTYPE_LOB );
1251 if ( r != OCI_SUCCESS )
1252 qWarning( "QOCISpatialCols: Cannot free LOB descriptor" );
1253 }
1254 }
1255
QOCISpatialCols(int size,QOCISpatialResultPrivate * dp)1256 QOCISpatialCols::QOCISpatialCols( int size, QOCISpatialResultPrivate *dp )
1257 : fieldInf( size ), d( dp )
1258 {
1259 ENTER
1260 ub4 dataSize = 0;
1261 OCIDefine *dfn = nullptr;
1262 int r;
1263
1264 OCIParam *param = nullptr;
1265 sb4 parmStatus = 0;
1266 ub4 count = 1;
1267 int idx = 0;
1268 parmStatus = OCIParamGet( d->sql,
1269 OCI_HTYPE_STMT,
1270 d->err,
1271 reinterpret_cast<void **>( ¶m ),
1272 count );
1273
1274 if ( parmStatus != OCI_SUCCESS )
1275 {
1276 qOraWarning( "OCIParamGet failed: ", d->err );
1277 }
1278
1279 while ( parmStatus == OCI_SUCCESS )
1280 {
1281 OraFieldInfo ofi = qMakeOraField( d, param );
1282 if ( ofi.oraType == SQLT_RDD )
1283 dataSize = 50;
1284 #ifdef SQLT_INTERVAL_YM
1285 #ifdef SQLT_INTERVAL_DS
1286 else if ( ofi.oraType == SQLT_INTERVAL_YM || ofi.oraType == SQLT_INTERVAL_DS )
1287 // since we are binding interval datatype as string,
1288 // we are not interested in the number of bytes but characters.
1289 dataSize = 50; // magic number
1290 #endif //SQLT_INTERVAL_DS
1291 #endif //SQLT_INTERVAL_YM
1292 else if ( ofi.oraType == SQLT_NUM || ofi.oraType == SQLT_VNU )
1293 {
1294 if ( ofi.oraPrecision > 0 )
1295 dataSize = ( ofi.oraPrecision + 1 ) * sizeof( utext );
1296 else
1297 dataSize = ( 38 + 1 ) * sizeof( utext );
1298 }
1299 else
1300 dataSize = ofi.oraLength;
1301
1302 fieldInf[idx].typ = ofi.type;
1303 fieldInf[idx].oraType = ofi.oraType;
1304 fieldInf[idx].oraTypeName = ofi.oraTypeName;
1305 rec.append( qFromOraInf( ofi ) );
1306
1307 qDebug() << "ofi.type:" << QVariant::typeToName( ofi.type );
1308 switch ( ofi.type )
1309 {
1310 case QVariant::DateTime:
1311 r = OCIDefineByPos( d->sql,
1312 &dfn,
1313 d->err,
1314 count,
1315 create( idx, dataSize + 1 ),
1316 dataSize + 1,
1317 SQLT_DAT,
1318 &( fieldInf[idx].ind ),
1319 nullptr, nullptr, OCI_DEFAULT );
1320 break;
1321 case QVariant::Double:
1322 r = OCIDefineByPos( d->sql,
1323 &dfn,
1324 d->err,
1325 count,
1326 create( idx, sizeof( double ) - 1 ),
1327 sizeof( double ),
1328 SQLT_FLT,
1329 &( fieldInf[idx].ind ),
1330 nullptr, nullptr, OCI_DEFAULT );
1331 break;
1332 case QVariant::Int:
1333 r = OCIDefineByPos( d->sql,
1334 &dfn,
1335 d->err,
1336 count,
1337 create( idx, sizeof( qint32 ) - 1 ),
1338 sizeof( qint32 ),
1339 SQLT_INT,
1340 &( fieldInf[idx].ind ),
1341 nullptr, nullptr, OCI_DEFAULT );
1342 break;
1343 case QVariant::LongLong:
1344 r = OCIDefineByPos( d->sql,
1345 &dfn,
1346 d->err,
1347 count,
1348 create( idx, sizeof( OCINumber ) ),
1349 sizeof( OCINumber ),
1350 SQLT_VNU,
1351 &( fieldInf[idx].ind ),
1352 nullptr, nullptr, OCI_DEFAULT );
1353 break;
1354 case QVariant::ByteArray:
1355 // RAW and LONG RAW fields can't be bound to LOB locators
1356 qDebug() << "ofi.oraType:" << ofi.oraType;
1357 if ( ofi.oraType == SQLT_BIN )
1358 {
1359 qDebug( "binding SQLT_BIN" );
1360 r = OCIDefineByPos( d->sql,
1361 &dfn,
1362 d->err,
1363 count,
1364 create( idx, dataSize ),
1365 dataSize,
1366 SQLT_BIN,
1367 &( fieldInf[idx].ind ),
1368 nullptr, nullptr, OCI_DYNAMIC_FETCH );
1369 }
1370 else if ( ofi.oraType == SQLT_LBI )
1371 {
1372 qDebug( "binding SQLT_LBI" );
1373 r = OCIDefineByPos( d->sql,
1374 &dfn,
1375 d->err,
1376 count,
1377 nullptr,
1378 SB4MAXVAL,
1379 SQLT_LBI,
1380 &( fieldInf[idx].ind ),
1381 nullptr, nullptr, OCI_DYNAMIC_FETCH );
1382 }
1383 else if ( ofi.oraType == SQLT_CLOB )
1384 {
1385 qDebug( "binding SQLT_CLOB" );
1386 r = OCIDefineByPos( d->sql,
1387 &dfn,
1388 d->err,
1389 count,
1390 createLobLocator( idx, d->env ),
1391 -1,
1392 SQLT_CLOB,
1393 &( fieldInf[idx].ind ),
1394 nullptr, nullptr, OCI_DEFAULT );
1395 }
1396 else if ( ofi.oraType == SQLT_NTY && ofi.oraTypeName == "SDO_GEOMETRY" )
1397 {
1398 qDebug( "binding SQLT_NTY SDO_GEOMETRY" );
1399 r = OCIDefineByPos( d->sql,
1400 &dfn,
1401 d->err,
1402 count,
1403 nullptr,
1404 0,
1405 SQLT_NTY,
1406 nullptr,
1407 nullptr,
1408 nullptr,
1409 OCI_DEFAULT );
1410
1411 if ( r == OCI_SUCCESS )
1412 {
1413 dp->sdoobj.push_back( 0 );
1414 dp->sdoind.push_back( 0 );
1415
1416 qDebug( "define object" );
1417 r = OCIDefineObject( dfn,
1418 d->err,
1419 ofi.oraOCIType,
1420 ( void ** ) & dp->sdoobj.last(), nullptr,
1421 ( void ** ) & dp->sdoind.last(), nullptr );
1422 }
1423 else
1424 {
1425 qOraWarning( "OCIDefineByPos failed: ", d->err );
1426 }
1427 }
1428 else
1429 {
1430 qDebug( "binding SQLT_BLOB" );
1431 r = OCIDefineByPos( d->sql,
1432 &dfn,
1433 d->err,
1434 count,
1435 createLobLocator( idx, d->env ),
1436 -1,
1437 SQLT_BLOB,
1438 &( fieldInf[idx].ind ),
1439 nullptr, nullptr, OCI_DEFAULT );
1440 }
1441 break;
1442 case QVariant::String:
1443 if ( ofi.oraType == SQLT_LNG )
1444 {
1445 qDebug( "binding SQLT_LNG" );
1446 r = OCIDefineByPos( d->sql,
1447 &dfn,
1448 d->err,
1449 count,
1450 nullptr,
1451 SB4MAXVAL,
1452 SQLT_LNG,
1453 &( fieldInf[idx].ind ),
1454 nullptr, nullptr, OCI_DYNAMIC_FETCH );
1455 }
1456 else
1457 {
1458 dataSize += dataSize + sizeof( QChar );
1459 qDebug( "OCIDefineByPosStr(%d): %d", count, dataSize );
1460 r = OCIDefineByPos( d->sql,
1461 &dfn,
1462 d->err,
1463 count,
1464 create( idx, dataSize ),
1465 dataSize,
1466 SQLT_STR,
1467 &( fieldInf[idx].ind ),
1468 nullptr, nullptr, OCI_DEFAULT );
1469 if ( r == OCI_SUCCESS )
1470 d->setCharset( dfn, OCI_HTYPE_DEFINE );
1471 }
1472 break;
1473 default:
1474 // this should make enough space even with character encoding
1475 dataSize = ( dataSize + 1 ) * sizeof( utext );
1476 qDebug( "OCIDefineByPosDef(%d): %d", count, dataSize );
1477 r = OCIDefineByPos( d->sql,
1478 &dfn,
1479 d->err,
1480 count,
1481 create( idx, dataSize ),
1482 dataSize + 1,
1483 SQLT_STR,
1484 &( fieldInf[idx].ind ),
1485 nullptr, nullptr, OCI_DEFAULT );
1486 break;
1487 }
1488
1489 if ( r != OCI_SUCCESS )
1490 qOraWarning( "QOCISpatialCols::bind:", d->err );
1491
1492 fieldInf[idx].def = dfn;
1493 ++count;
1494 ++idx;
1495 parmStatus = OCIParamGet( d->sql,
1496 OCI_HTYPE_STMT,
1497 d->err,
1498 reinterpret_cast<void **>( ¶m ),
1499 count );
1500 }
1501 }
1502
~QOCISpatialCols()1503 QOCISpatialCols::~QOCISpatialCols()
1504 {
1505 ENTER
1506 }
1507
create(int position,int size)1508 char *QOCISpatialCols::create( int position, int size )
1509 {
1510 ENTER
1511 char *c = new char[size + 1];
1512 // Oracle may not fill fixed width fields
1513 memset( c, 0, size + 1 );
1514 fieldInf[position].data = c;
1515 fieldInf[position].len = size;
1516 return c;
1517 }
1518
createLobLocator(int position,OCIEnv * env)1519 OCILobLocator **QOCISpatialCols::createLobLocator( int position, OCIEnv *env )
1520 {
1521 ENTER
1522 OCILobLocator *&lob = fieldInf[position].lob;
1523 int r = OCIDescriptorAlloc( env,
1524 reinterpret_cast<void **>( &lob ),
1525 OCI_DTYPE_LOB,
1526 0,
1527 nullptr );
1528 if ( r != OCI_SUCCESS )
1529 {
1530 qWarning( "QOCISpatialCols: Cannot create LOB locator" );
1531 lob = nullptr;
1532 }
1533 return &lob;
1534 }
1535
readPiecewise(QVector<QVariant> & values,int index)1536 int QOCISpatialCols::readPiecewise( QVector<QVariant> &values, int index )
1537 {
1538 ENTER
1539 qDebug() << "readPiecewise( index =" << index << " )";
1540 OCIDefine *dfn = nullptr;
1541 ub4 typep;
1542 ub1 in_outp;
1543 ub4 iterp;
1544 ub4 idxp;
1545 ub1 piecep;
1546 sword status;
1547 text col [QOCISPATIAL_DYNAMIC_CHUNK_SIZE + 1];
1548 int fieldNum = -1;
1549 int r = 0;
1550 bool nullField;
1551
1552 do
1553 {
1554 r = OCIStmtGetPieceInfo( d->sql, d->err, reinterpret_cast<void **>( &dfn ), &typep,
1555 &in_outp, &iterp, &idxp, &piecep );
1556 if ( r != OCI_SUCCESS )
1557 qOraWarning( "QOCISpatialResultPrivate::readPiecewise: unable to get piece info:", d->err );
1558 fieldNum = fieldFromDefine( dfn );
1559 bool isStringField = fieldInf.at( fieldNum ).oraType == SQLT_LNG;
1560 ub4 chunkSize = QOCISPATIAL_DYNAMIC_CHUNK_SIZE;
1561 nullField = false;
1562 r = OCIStmtSetPieceInfo( dfn, OCI_HTYPE_DEFINE,
1563 d->err, col,
1564 &chunkSize, piecep, nullptr, nullptr );
1565 if ( r != OCI_SUCCESS )
1566 qOraWarning( "QOCISpatialResultPrivate::readPiecewise: unable to set piece info:", d->err );
1567 status = OCIStmtFetch( d->sql, d->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT );
1568 if ( status == -1 )
1569 {
1570 sb4 errcode;
1571 OCIErrorGet( d->err, 1, nullptr, &errcode, nullptr, 0, OCI_HTYPE_ERROR );
1572 switch ( errcode )
1573 {
1574 case 1405: /* NULL */
1575 nullField = true;
1576 break;
1577 default:
1578 qOraWarning( "QOCISpatialResultPrivate::readPiecewise: unable to fetch next:", d->err );
1579 break;
1580 }
1581 }
1582 if ( status == OCI_NO_DATA )
1583 break;
1584 if ( nullField || !chunkSize )
1585 {
1586 fieldInf[fieldNum].ind = -1;
1587 }
1588 else
1589 {
1590 if ( isStringField )
1591 {
1592 QString str = values.at( fieldNum + index ).toString();
1593 str += QString( reinterpret_cast<const QChar *>( col ), chunkSize / 2 );
1594 values[fieldNum + index] = str;
1595 fieldInf[fieldNum].ind = 0;
1596 }
1597 else
1598 {
1599 QByteArray ba = values.at( fieldNum + index ).toByteArray();
1600 int sz = ba.size();
1601 ba.resize( sz + chunkSize );
1602 memcpy( ba.data() + sz, reinterpret_cast<char *>( col ), chunkSize );
1603 values[fieldNum + index] = ba;
1604 fieldInf[fieldNum].ind = 0;
1605 }
1606 }
1607 }
1608 while ( status == OCI_SUCCESS_WITH_INFO || status == OCI_NEED_DATA );
1609 return r;
1610 }
1611
qMakeOraField(const QOCISpatialResultPrivate * p,OCIParam * param) const1612 OraFieldInfo QOCISpatialCols::qMakeOraField( const QOCISpatialResultPrivate *p, OCIParam *param ) const
1613 {
1614 ENTER
1615
1616 OraFieldInfo ofi;
1617 ub2 colType( 0 );
1618 text *colName = nullptr;
1619 ub4 colNameLen( 0 );
1620 sb1 colScale( 0 );
1621 ub2 colLength( 0 );
1622 ub2 colFieldLength( 0 );
1623 sb2 colPrecision( 0 );
1624 ub1 colIsNull( 0 );
1625 text *colTypeName = nullptr;
1626 ub4 colTypeNameLen( 0 );
1627 OCIType *colOCIType = nullptr;
1628 int r( 0 );
1629 QVariant::Type type( QVariant::Invalid );
1630
1631 r = OCIAttrGet( param,
1632 OCI_DTYPE_PARAM,
1633 &colType,
1634 nullptr,
1635 OCI_ATTR_DATA_TYPE,
1636 p->err );
1637 if ( r != OCI_SUCCESS )
1638 qOraWarning( "qMakeOraField:", p->err );
1639
1640 r = OCIAttrGet( param,
1641 OCI_DTYPE_PARAM,
1642 &colName,
1643 &colNameLen,
1644 OCI_ATTR_NAME,
1645 p->err );
1646 if ( r != OCI_SUCCESS )
1647 qOraWarning( "qMakeOraField:", p->err );
1648
1649 r = OCIAttrGet( param,
1650 OCI_DTYPE_PARAM,
1651 &colLength,
1652 nullptr,
1653 OCI_ATTR_DATA_SIZE, /* in bytes */
1654 p->err );
1655 if ( r != OCI_SUCCESS )
1656 qOraWarning( "qMakeOraField:", p->err );
1657
1658 #ifdef OCI_ATTR_CHAR_SIZE
1659 r = OCIAttrGet( param,
1660 OCI_DTYPE_PARAM,
1661 &colFieldLength,
1662 nullptr,
1663 OCI_ATTR_CHAR_SIZE,
1664 p->err );
1665 if ( r != OCI_SUCCESS )
1666 qOraWarning( "qMakeOraField:", p->err );
1667 #else
1668 // for Oracle8.
1669 colFieldLength = colLength;
1670 #endif
1671
1672 r = OCIAttrGet( param,
1673 OCI_DTYPE_PARAM,
1674 &colPrecision,
1675 nullptr,
1676 OCI_ATTR_PRECISION,
1677 p->err );
1678 if ( r != OCI_SUCCESS )
1679 qOraWarning( "qMakeOraField:", p->err );
1680
1681 r = OCIAttrGet( param,
1682 OCI_DTYPE_PARAM,
1683 &colScale,
1684 nullptr,
1685 OCI_ATTR_SCALE,
1686 p->err );
1687 if ( r != OCI_SUCCESS )
1688 qOraWarning( "qMakeOraField:", p->err );
1689
1690 r = OCIAttrGet( param,
1691 OCI_DTYPE_PARAM,
1692 &colType,
1693 nullptr,
1694 OCI_ATTR_DATA_TYPE,
1695 p->err );
1696 if ( r != OCI_SUCCESS )
1697 qOraWarning( "qMakeOraField:", p->err );
1698
1699 qDebug() << "colType:" << colLength;
1700
1701 r = OCIAttrGet( param,
1702 OCI_DTYPE_PARAM,
1703 &colIsNull,
1704 nullptr,
1705 OCI_ATTR_IS_NULL,
1706 p->err );
1707 if ( r != OCI_SUCCESS )
1708 qOraWarning( "qMakeOraField:", p->err );
1709
1710 if ( colType == SQLT_NTY )
1711 {
1712 qDebug() << "NTY!";
1713 r = OCIAttrGet( param,
1714 OCI_DTYPE_PARAM,
1715 &colTypeName,
1716 &colTypeNameLen,
1717 OCI_ATTR_TYPE_NAME,
1718 p->err );
1719 if ( r != OCI_SUCCESS )
1720 qOraWarning( "qMakeOraField:", p->err );
1721
1722 qDebug() << "typename: " << QString( reinterpret_cast<const QChar *>( colTypeName ), colTypeNameLen / 2 );
1723
1724 OCIRef *typeRef = nullptr;
1725
1726 r = OCIAttrGet( param,
1727 OCI_DTYPE_PARAM,
1728 &typeRef,
1729 nullptr,
1730 OCI_ATTR_REF_TDO,
1731 p->err );
1732 if ( r != OCI_SUCCESS )
1733 qOraWarning( "qMakeOraField:", p->err );
1734
1735 r = OCIObjectPin( d->env, d->err, typeRef, nullptr, OCI_PIN_ANY, OCI_DURATION_SESSION, OCI_LOCK_NONE, ( void ** ) & colOCIType );
1736 if ( r != OCI_SUCCESS )
1737 qOraWarning( "qMakeOraField:", d->err );
1738 }
1739
1740 type = qDecodeOCIType( colType, p->q_func()->numericalPrecisionPolicy() );
1741
1742 if ( type == QVariant::Int )
1743 {
1744 if ( colLength == 22 && colPrecision == 0 && colScale == 0 )
1745 type = QVariant::String;
1746 if ( colScale > 0 )
1747 type = QVariant::String;
1748 }
1749
1750 // bind as double if the precision policy asks for it
1751 if ( ( ( colType == SQLT_FLT ) || ( colType == SQLT_NUM ) )
1752 && ( p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble ) )
1753 {
1754 type = QVariant::Double;
1755 }
1756
1757 // bind as int32 or int64 if the precision policy asks for it
1758 if ( ( colType == SQLT_NUM ) || ( colType == SQLT_VNU ) || ( colType == SQLT_UIN )
1759 || ( colType == SQLT_INT ) )
1760 {
1761 if ( p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64 )
1762 type = QVariant::LongLong;
1763 else if ( p->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32 )
1764 type = QVariant::Int;
1765 }
1766
1767 if ( colType == SQLT_BLOB )
1768 colLength = 0;
1769
1770 // colNameLen is length in bytes
1771 ofi.name = QString( reinterpret_cast<const QChar *>( colName ), colNameLen / 2 );
1772 ofi.type = type;
1773 ofi.oraType = colType;
1774 ofi.oraFieldLength = colFieldLength;
1775 ofi.oraLength = colLength;
1776 ofi.oraScale = colScale;
1777 ofi.oraPrecision = colPrecision;
1778 ofi.oraIsNull = colIsNull;
1779 ofi.oraTypeName = QString( reinterpret_cast<const QChar *>( colTypeName ), colTypeNameLen / 2 );
1780 ofi.oraOCIType = colOCIType;
1781
1782 #ifdef QOCISPATIAL_DEBUG
1783 qDebug() << "name: " << ofi.name
1784 << "\ntype:" << ofi.type
1785 << "\noraType:" << ofi.oraType
1786 << "\noraFieldLength:" << ofi.oraFieldLength
1787 << "\noraLength:" << ofi.oraLength
1788 << "\noraScale:" << ofi.oraScale
1789 << "\noraPrecision:" << ofi.oraPrecision
1790 << "\noraIsNull:" << ofi.oraIsNull
1791 << "\noraTypeName:" << ofi.oraTypeName
1792 << "\n----------------------\n"
1793 ;
1794 #endif
1795
1796 return ofi;
1797 }
1798
1799 struct QOCISpatialBatchColumn
1800 {
1801 inline QOCISpatialBatchColumn() = default;
1802
1803 OCIBind *bindh = nullptr;
1804 ub2 bindAs = 0;
1805 ub4 maxLen = 0;
1806 ub4 recordCount = 0;
1807 std::vector<char> data;
1808 std::vector<ub2> lengths;
1809 std::vector<sb2> indicators;
1810 ub4 maxarr_len = 0;
1811 ub4 curelep = 0;
1812 };
1813
execBatch(QOCISpatialResultPrivate * d,QVector<QVariant> & boundValues,bool arrayBind)1814 bool QOCISpatialCols::execBatch( QOCISpatialResultPrivate *d, QVector<QVariant> &boundValues, bool arrayBind )
1815 {
1816 ENTER
1817
1818 int columnCount = boundValues.count();
1819 if ( boundValues.isEmpty() || columnCount == 0 )
1820 return false;
1821
1822 #ifdef QOCISPATIAL_DEBUG
1823 qDebug() << "columnCount:" << columnCount << boundValues;
1824 #endif
1825
1826 int i;
1827 sword r;
1828
1829 QVarLengthArray<QVariant::Type> fieldTypes;
1830 for ( i = 0; i < columnCount; ++i )
1831 {
1832 QVariant::Type tp = boundValues.at( i ).type();
1833 fieldTypes.append( tp == QVariant::List ? boundValues.at( i ).toList().value( 0 ).type()
1834 : tp );
1835 }
1836
1837 QList<QByteArray> tmpStorage;
1838 SizeArray tmpSizes( columnCount );
1839 QVector<QOCISpatialBatchColumn> columns( columnCount );
1840
1841 // figuring out buffer sizes
1842 for ( i = 0; i < columnCount; ++i )
1843 {
1844
1845 if ( boundValues.at( i ).type() != QVariant::List )
1846 {
1847
1848 // not a list - create a deep-copy of the single value
1849 QOCISpatialBatchColumn &singleCol = columns[i];
1850 singleCol.indicators.resize( 1 );
1851 singleCol.indicators[0] = boundValues.at( i ).isNull() ? -1 : 0;
1852
1853 r = d->bindValue( d->sql, &singleCol.bindh, d->err, i,
1854 boundValues.at( i ), &singleCol.indicators[0], &tmpSizes[i], tmpStorage );
1855
1856 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
1857 {
1858 qOraWarning( "QOCISpatialPrivate::execBatch: unable to bind column:", d->err );
1859 d->q_func()->setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
1860 "Unable to bind column for batch execute" ),
1861 QSqlError::StatementError, d->err ) );
1862 return false;
1863 }
1864 continue;
1865 }
1866
1867 QOCISpatialBatchColumn &col = columns[i];
1868 col.recordCount = boundValues.at( i ).toList().count();
1869
1870 col.lengths.resize( col.recordCount );
1871 col.indicators.resize( col.recordCount );
1872 col.maxarr_len = col.recordCount;
1873 col.curelep = col.recordCount;
1874
1875 switch ( fieldTypes[i] )
1876 {
1877 case QVariant::Time:
1878 case QVariant::Date:
1879 case QVariant::DateTime:
1880 col.bindAs = SQLT_DAT;
1881 col.maxLen = 7;
1882 break;
1883
1884 case QVariant::Int:
1885 col.bindAs = SQLT_INT;
1886 col.maxLen = sizeof( int );
1887 break;
1888
1889 case QVariant::UInt:
1890 col.bindAs = SQLT_UIN;
1891 col.maxLen = sizeof( uint );
1892 break;
1893
1894 case QVariant::LongLong:
1895 col.bindAs = SQLT_VNU;
1896 col.maxLen = sizeof( OCINumber );
1897 break;
1898
1899 case QVariant::ULongLong:
1900 col.bindAs = SQLT_VNU;
1901 col.maxLen = sizeof( OCINumber );
1902 break;
1903
1904 case QVariant::Double:
1905 col.bindAs = SQLT_FLT;
1906 col.maxLen = sizeof( double );
1907 break;
1908
1909 case QVariant::UserType:
1910 col.bindAs = SQLT_RDD;
1911 col.maxLen = sizeof( OCIRowid * );
1912 break;
1913
1914 case QVariant::String:
1915 {
1916 col.bindAs = SQLT_STR;
1917 for ( uint j = 0; j < col.recordCount; ++j )
1918 {
1919 uint len;
1920 if ( d->isOutValue( i ) )
1921 len = boundValues.at( i ).toList().at( j ).toString().capacity() + 1;
1922 else
1923 len = boundValues.at( i ).toList().at( j ).toString().length() + 1;
1924 if ( len > col.maxLen )
1925 col.maxLen = len;
1926 }
1927 col.maxLen *= sizeof( QChar );
1928 break;
1929 }
1930
1931 case QVariant::ByteArray:
1932 default:
1933 {
1934 col.bindAs = SQLT_LBI;
1935 for ( uint j = 0; j < col.recordCount; ++j )
1936 {
1937 if ( d->isOutValue( i ) )
1938 col.lengths[j] = boundValues.at( i ).toList().at( j ).toByteArray().capacity();
1939 else
1940 col.lengths[j] = boundValues.at( i ).toList().at( j ).toByteArray().size();
1941 if ( col.lengths[j] > col.maxLen )
1942 col.maxLen = col.lengths[j];
1943 }
1944 break;
1945 }
1946 }
1947
1948 col.data.resize( col.maxLen * col.recordCount );
1949
1950 // we may now populate column with data
1951 for ( uint row = 0; row < col.recordCount; ++row )
1952 {
1953 const QVariant &val = boundValues.at( i ).toList().at( row );
1954
1955 if ( val.isNull() )
1956 {
1957 columns[i].indicators[row] = -1;
1958 columns[i].lengths[row] = 0;
1959 }
1960 else
1961 {
1962 columns[i].indicators[row] = 0;
1963 char *dataPtr = &columns[i].data[0] + ( columns[i].maxLen * row );
1964 switch ( fieldTypes[i] )
1965 {
1966 case QVariant::Time:
1967 case QVariant::Date:
1968 case QVariant::DateTime:
1969 {
1970 columns[i].lengths[row] = columns[i].maxLen;
1971 const QByteArray ba = qMakeOraDate( val.toDateTime() );
1972 Q_ASSERT( ba.size() == int( columns[i].maxLen ) );
1973 memcpy( dataPtr, ba.constData(), columns[i].maxLen );
1974 break;
1975 }
1976 case QVariant::Int:
1977 columns[i].lengths[row] = columns[i].maxLen;
1978 *reinterpret_cast<int *>( dataPtr ) = val.toInt();
1979 break;
1980
1981 case QVariant::UInt:
1982 columns[i].lengths[row] = columns[i].maxLen;
1983 *reinterpret_cast<uint *>( dataPtr ) = val.toUInt();
1984 break;
1985
1986 case QVariant::LongLong:
1987 {
1988 columns[i].lengths[row] = columns[i].maxLen;
1989 const QByteArray ba = qMakeOCINumber( val.toLongLong(), d->err );
1990 Q_ASSERT( ba.size() == int( columns[i].maxLen ) );
1991 memcpy( dataPtr, ba.constData(), columns[i].maxLen );
1992 break;
1993 }
1994 case QVariant::ULongLong:
1995 {
1996 columns[i].lengths[row] = columns[i].maxLen;
1997 const QByteArray ba = qMakeOCINumber( val.toULongLong(), d->err );
1998 Q_ASSERT( ba.size() == int( columns[i].maxLen ) );
1999 memcpy( dataPtr, ba.constData(), columns[i].maxLen );
2000 break;
2001 }
2002 case QVariant::Double:
2003 columns[i].lengths[row] = columns[i].maxLen;
2004 *reinterpret_cast<double *>( dataPtr ) = val.toDouble();
2005 break;
2006
2007 case QVariant::String:
2008 {
2009 const QString s = val.toString();
2010 columns[i].lengths[row] = ( ub2 )( s.length() + 1 ) * sizeof( QChar );
2011 memcpy( dataPtr, s.utf16(), columns[i].lengths[row] );
2012 break;
2013 }
2014 case QVariant::UserType:
2015 if ( val.canConvert<QOCISpatialRowIdPointer>() )
2016 {
2017 const QOCISpatialRowIdPointer rptr = qvariant_cast<QOCISpatialRowIdPointer>( val );
2018 *reinterpret_cast<OCIRowid **>( dataPtr ) = rptr->id;
2019 columns[i].lengths[row] = 0;
2020 break;
2021 }
2022
2023 FALLTHROUGH
2024
2025 case QVariant::ByteArray:
2026 default:
2027 {
2028 const QByteArray ba = val.toByteArray();
2029 columns[i].lengths[row] = ba.size();
2030 memcpy( dataPtr, ba.constData(), ba.size() );
2031 break;
2032 }
2033 }
2034 }
2035 }
2036
2037 QOCISpatialBatchColumn &bindColumn = columns[i];
2038
2039 #ifdef QOCISPATIAL_DEBUG
2040 qDebug( "OCIBindByPos(%p, %p, %p, %d, %p, %d, %d, %p, %p, 0, %d, %p, OCI_DEFAULT)",
2041 d->sql, &bindColumn.bindh, d->err, i + 1, bindColumn.data,
2042 bindColumn.maxLen, bindColumn.bindAs, bindColumn.indicators, bindColumn.lengths,
2043 arrayBind ? bindColumn.maxarr_len : 0, arrayBind ? &bindColumn.curelep : 0 );
2044
2045 for ( int ii = 0; ii < static_cast<int>( bindColumn.recordCount ); ++ii )
2046 {
2047 qDebug( " record %d: indicator %d, length %d", ii, bindColumn.indicators[ii],
2048 bindColumn.lengths[ii] );
2049 }
2050 #endif
2051
2052
2053 // binding the column
2054 r = OCIBindByPos(
2055 d->sql, &bindColumn.bindh, d->err, i + 1,
2056 &bindColumn.data[0],
2057 bindColumn.maxLen,
2058 bindColumn.bindAs,
2059 &bindColumn.indicators[0],
2060 &bindColumn.lengths[0],
2061 nullptr,
2062 arrayBind ? bindColumn.maxarr_len : 0,
2063 arrayBind ? &bindColumn.curelep : nullptr,
2064 OCI_DEFAULT );
2065
2066 #ifdef QOCISPATIAL_DEBUG
2067 qDebug( "After OCIBindByPos: r = %d, bindh = %p", r, bindColumn.bindh );
2068 #endif
2069
2070 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
2071 {
2072 qOraWarning( "QOCISpatialPrivate::execBatch: unable to bind column:", d->err );
2073 d->q_func()->setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
2074 "Unable to bind column for batch execute" ),
2075 QSqlError::StatementError, d->err ) );
2076 return false;
2077 }
2078
2079 r = OCIBindArrayOfStruct(
2080 columns[i].bindh, d->err,
2081 columns[i].maxLen,
2082 sizeof( columns[i].indicators[0] ),
2083 sizeof( columns[i].lengths[0] ),
2084 0 );
2085
2086 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
2087 {
2088 qOraWarning( "QOCISpatialPrivate::execBatch: unable to bind column:", d->err );
2089 d->q_func()->setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
2090 "Unable to bind column for batch execute" ),
2091 QSqlError::StatementError, d->err ) );
2092 return false;
2093 }
2094 }
2095
2096 //finally we can execute
2097 r = OCIStmtExecute( d->svc, d->sql, d->err,
2098 arrayBind ? 1 : columns[0].recordCount,
2099 0, nullptr, nullptr,
2100 d->transaction || !d->commitOnSuccess ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS );
2101
2102 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
2103 {
2104 qOraWarning( "QOCISpatialPrivate::execBatch: unable to execute batch statement:", d->err );
2105 d->q_func()->setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
2106 "Unable to execute batch statement" ),
2107 QSqlError::StatementError, d->err ) );
2108 return false;
2109 }
2110
2111 // for out parameters we copy data back to value vector
2112 for ( i = 0; i < columnCount; ++i )
2113 {
2114
2115 if ( !d->isOutValue( i ) )
2116 continue;
2117
2118 QVariant::Type tp = boundValues.at( i ).type();
2119 if ( tp != QVariant::List )
2120 {
2121 qOraOutValue( boundValues[i], tmpStorage, d->err );
2122 if ( columns[i].indicators[0] == -1 )
2123 boundValues[i] = QVariant( tp );
2124 continue;
2125 }
2126
2127 QVariantList *list = static_cast<QVariantList *>( const_cast<void *>( boundValues.at( i ).data() ) );
2128
2129 const char *data = columns[i].data.data();
2130 for ( uint r = 0; r < columns[i].recordCount; ++r )
2131 {
2132
2133 if ( columns[i].indicators[r] == -1 )
2134 {
2135 ( *list )[r] = QVariant();
2136 continue;
2137 }
2138
2139 switch ( columns[i].bindAs )
2140 {
2141
2142 case SQLT_DAT:
2143 ( *list )[r] = qMakeDate( data + r * columns[i].maxLen );
2144 break;
2145
2146 case SQLT_INT:
2147 ( *list )[r] = *reinterpret_cast<const int *>( data + r * columns[i].maxLen );
2148 break;
2149
2150 case SQLT_UIN:
2151 ( *list )[r] = *reinterpret_cast<const uint *>( data + r * columns[i].maxLen );
2152 break;
2153
2154 case SQLT_VNU:
2155 {
2156 switch ( boundValues.at( i ).type() )
2157 {
2158 case QVariant::LongLong:
2159 ( *list )[r] = qMakeLongLong( data + r * columns[i].maxLen, d->err );
2160 break;
2161 case QVariant::ULongLong:
2162 ( *list )[r] = qMakeULongLong( data + r * columns[i].maxLen, d->err );
2163 break;
2164 default:
2165 break;
2166 }
2167 break;
2168 }
2169
2170 case SQLT_FLT:
2171 ( *list )[r] = *reinterpret_cast<const double *>( data + r * columns[i].maxLen );
2172 break;
2173
2174 case SQLT_STR:
2175 ( *list )[r] = QString( reinterpret_cast<const QChar *>( data
2176 + r * columns[i].maxLen ) );
2177 break;
2178
2179 default:
2180 ( *list )[r] = QByteArray( data + r * columns[i].maxLen, columns[i].maxLen );
2181 break;
2182 }
2183 }
2184 }
2185
2186 d->q_func()->setSelect( false );
2187 d->q_func()->setAt( QSql::BeforeFirstRow );
2188 d->q_func()->setActive( true );
2189
2190 return true;
2191 }
2192
2193 template<class T, int sz>
qReadLob(T & buf,const QOCISpatialResultPrivate * d,OCILobLocator * lob)2194 int qReadLob( T &buf, const QOCISpatialResultPrivate *d, OCILobLocator *lob )
2195 {
2196 ENTER
2197 ub1 csfrm;
2198 ub4 amount;
2199 int r;
2200
2201 // Read this from the database, don't assume we know what it is set to
2202 r = OCILobCharSetForm( d->env, d->err, lob, &csfrm );
2203 if ( r != OCI_SUCCESS )
2204 {
2205 qOraWarning( "OCIResultPrivate::readLobs: Couldn't get LOB char set form: ", d->err );
2206 csfrm = 0;
2207 }
2208
2209 // Get the length of the LOB (this is in characters)
2210 r = OCILobGetLength( d->svc, d->err, lob, &amount );
2211 if ( r == OCI_SUCCESS )
2212 {
2213 if ( amount == 0 )
2214 {
2215 // Short cut for null LOBs
2216 buf.resize( 0 );
2217 return OCI_SUCCESS;
2218 }
2219 }
2220 else
2221 {
2222 qOraWarning( "OCIResultPrivate::readLobs: Couldn't get LOB length: ", d->err );
2223 return r;
2224 }
2225
2226 // Resize the buffer to hold the LOB contents
2227 buf.resize( amount );
2228
2229 // Read the LOB into the buffer
2230 r = OCILobRead( d->svc,
2231 d->err,
2232 lob,
2233 &amount,
2234 1,
2235 buf.data(),
2236 buf.size() * sz, // this argument is in bytes, not characters
2237 nullptr,
2238 nullptr,
2239 // Extract the data from a CLOB in UTF-16 (ie. what QString uses internally)
2240 sz == 1 ? ub2( 0 ) : ub2( QOCISpatialEncoding ),
2241 csfrm );
2242
2243 if ( r != OCI_SUCCESS )
2244 qOraWarning( "OCIResultPrivate::readLOBs: Cannot read LOB: ", d->err );
2245
2246 return r;
2247 }
2248
readLOBs(QVector<QVariant> & values,int index)2249 int QOCISpatialCols::readLOBs( QVector<QVariant> &values, int index )
2250 {
2251 ENTER
2252 OCILobLocator *lob = nullptr;
2253 int r = OCI_SUCCESS;
2254
2255 for ( int i = 0; i < size(); ++i )
2256 {
2257 const OraFieldInf &fi = fieldInf.at( i );
2258 if ( fi.ind == -1 || !( lob = fi.lob ) )
2259 continue;
2260
2261 bool isClob = fi.oraType == SQLT_CLOB;
2262 QVariant var;
2263
2264 if ( isClob )
2265 {
2266 QString str;
2267 r = qReadLob < QString, sizeof( QChar ) > ( str, d, lob );
2268 var = str;
2269 }
2270 else
2271 {
2272 QByteArray buf;
2273 r = qReadLob < QByteArray, sizeof( char ) > ( buf, d, lob );
2274 var = buf;
2275 }
2276 if ( r == OCI_SUCCESS )
2277 values[index + i] = var;
2278 else
2279 break;
2280 }
2281 return r;
2282 }
2283
fieldFromDefine(OCIDefine * d)2284 int QOCISpatialCols::fieldFromDefine( OCIDefine *d )
2285 {
2286 ENTER
2287 for ( int i = 0; i < fieldInf.count(); ++i )
2288 {
2289 if ( fieldInf.at( i ).def == d )
2290 return i;
2291 }
2292 return -1;
2293 }
2294
getValue(OCINumber * num,unsigned int & value)2295 bool QOCISpatialCols::getValue( OCINumber *num, unsigned int &value )
2296 {
2297 if ( OCINumberToInt( d->err, num, sizeof( unsigned int ), OCI_NUMBER_UNSIGNED, &value ) == OCI_SUCCESS )
2298 return true;
2299
2300 qOraWarning( "Couldn't convert number to uint: ", d->err );
2301 return false;
2302 }
2303
getValue(OCINumber * num,int & value)2304 bool QOCISpatialCols::getValue( OCINumber *num, int &value )
2305 {
2306 if ( OCINumberToInt( d->err, num, sizeof( int ), OCI_NUMBER_SIGNED, &value ) == OCI_SUCCESS )
2307 return true;
2308
2309 qOraWarning( "Couldn't convert number to int: ", d->err );
2310 return false;
2311 }
2312
getValue(OCINumber * num,double & value)2313 bool QOCISpatialCols::getValue( OCINumber *num, double &value )
2314 {
2315 if ( OCINumberToReal( d->err, num, sizeof( double ), &value ) == OCI_SUCCESS )
2316 return true;
2317
2318 qOraWarning( "Couldn't convert number to double: ", d->err );
2319 return false;
2320 }
2321
getArraySize(OCIColl * coll,int & nSize)2322 bool QOCISpatialCols::getArraySize( OCIColl *coll, int &nSize )
2323 {
2324 if ( OCICollSize( d->env, d->err, coll, &nSize ) == OCI_SUCCESS )
2325 return true;
2326
2327 qOraWarning( "Couldn't not get elem_info collection size: ", d->err );
2328 return false;
2329 }
2330
getElemInfoElem(int iElem,const QVector<int> & vElems,int nOrds,int & startOffset,int & endOffset,int & etype,int & interpretation)2331 bool QOCISpatialCols::getElemInfoElem( int iElem, const QVector<int> &vElems, int nOrds,
2332 int &startOffset, int &endOffset,
2333 int &etype, int &interpretation )
2334 {
2335 startOffset = vElems[ iElem + 0 ];
2336 etype = vElems[ iElem + 1 ];
2337 interpretation = vElems[ iElem + 2 ];
2338
2339 if ( iElem + 3 >= vElems.size() )
2340 {
2341 endOffset = nOrds + 1;
2342 }
2343 else
2344 {
2345 endOffset = vElems[ iElem + 3 ];
2346 }
2347
2348 --startOffset;
2349 --endOffset;
2350
2351 return true;
2352 }
2353
getCurveParts(int & iElem,const QVector<int> & vElems,int nOrds,const QVector<double> & ordinates,int nDims,WKBType & baseType,bool & ok)2354 QOCISpatialCols::CurveParts QOCISpatialCols::getCurveParts( int &iElem, const QVector<int> &vElems, int nOrds,
2355 const QVector<double> &ordinates, int nDims,
2356 WKBType &baseType, bool &ok )
2357 {
2358 ok = true;
2359 int startOffset, endOffset, etype, n;
2360 if ( !getElemInfoElem( iElem, vElems, nOrds, startOffset, endOffset, etype, n ) )
2361 {
2362 qWarning() << "could not fetch element info" << iElem;
2363 ok = false;
2364 return CurveParts();
2365 }
2366
2367 if ( etype == 2 && ( n == 1 || n == 2 ) )
2368 {
2369 // LineString (n == 1) or CircularString (n == 2)
2370 baseType = ( n == 1 ) ? ( nDims == 2 ? WKBLineString : WKBLineString25D ) : ( nDims == 2 ? WKBCircularString : WKBCircularStringZ );
2371
2372 PointSequence points;
2373 points.reserve( 1 + ( endOffset - startOffset ) / nDims );
2374 for ( int j = startOffset; j < endOffset; j += nDims )
2375 {
2376 double x = ordinates[ j ];
2377 double y = ordinates[ j + 1 ];
2378 double z = nDims > 2 ? ordinates[ j + 2] : 0;
2379 points << Point( x, y, z );
2380 }
2381 return ( CurveParts() << qMakePair( baseType, points ) );
2382 }
2383 else if ( etype == 4 && n > 1 )
2384 {
2385 // CompoundCurve
2386 baseType = nDims == 2 ? WKBCompoundCurve : WKBCompoundCurveZ;
2387 int compoundParts = n;
2388 CurveParts parts;
2389 for ( int k = 0; k < compoundParts; k += 1 )
2390 {
2391 iElem += 3;
2392 if ( !getElemInfoElem( iElem, vElems, nOrds, startOffset, endOffset, etype, n ) )
2393 {
2394 qWarning() << "could not fetch element info" << iElem;
2395 return CurveParts();
2396 }
2397
2398 if ( etype == 2 && ( n == 1 || n == 2 ) )
2399 {
2400 WKBType partType = ( n == 1 ) ? ( nDims == 2 ? WKBLineString : WKBLineString25D ) : ( nDims == 2 ? WKBCircularString : WKBCircularStringZ );
2401 PointSequence points;
2402 points.reserve( 1 + ( endOffset - startOffset ) / nDims );
2403 for ( int j = startOffset; j < endOffset; j += nDims )
2404 {
2405 double x = ordinates[ j ];
2406 double y = ordinates[ j + 1 ];
2407 double z = nDims > 2 ? ordinates[ j + 2] : 0;
2408 points << Point( x, y, z );
2409 }
2410 parts << qMakePair( partType, points );
2411 }
2412 else
2413 {
2414 qWarning( "skipped unsupported compound curve element: etype=%08x n=%d", etype, n );
2415 }
2416 }
2417 return parts;
2418 }
2419 else
2420 {
2421 qWarning( "skipped unsupported line element: etype=%08x n=%d", etype, n );
2422 return CurveParts();
2423 }
2424 }
2425
convertToWkb(QVariant & v,int index)2426 bool QOCISpatialCols::convertToWkb( QVariant &v, int index )
2427 {
2428 ENTER
2429
2430 Q_ASSERT( index < d->sdoobj.size() );
2431 Q_ASSERT( index < d->sdoind.size() );
2432
2433 QOCISDOGeometryObj *sdoobj = d->sdoobj[index];
2434 QOCISDOGeometryInd *sdoind = d->sdoind[index];
2435
2436 qDebug() << "sdoobj =" << sdoobj;
2437 qDebug() << "sdoinf =" << sdoind;
2438 if ( sdoind )
2439 qDebug() << "sdoind->_atomic =" << sdoind->_atomic;
2440
2441 v = QVariant( QVariant::ByteArray );
2442
2443 if ( !sdoobj || !sdoind )
2444 {
2445 qDebug() << "sdoobj or sdoind not set";
2446 return false;
2447 }
2448
2449 if ( sdoind->_atomic == OCI_IND_NULL )
2450 {
2451 qDebug() << "geometry is NULL";
2452 return true;
2453 }
2454
2455 unsigned int iGType;
2456 if ( !getValue( &sdoobj->gtype, iGType ) )
2457 return false;
2458
2459 int nDims = SDO_GTYPE_D( iGType );
2460 int iType = SDO_GTYPE_TT( iGType );
2461 qDebug() << " d =" << nDims;
2462 qDebug() << " tt =" << iType;
2463
2464 if ( SDO_GTYPE_L( iGType ) != 0 )
2465 {
2466 qWarning() << "LRS" << SDO_GTYPE_L( iGType ) << "ignored";
2467 }
2468
2469 int iSrid = 0;
2470 if ( sdoind->srid == OCI_IND_NOTNULL )
2471 {
2472 if ( !getValue( &sdoobj->srid, iSrid ) )
2473 return false;
2474 }
2475
2476 qDebug() << " srid =" << iSrid;
2477
2478 QByteArray ba;
2479 union wkbPtr ptr;
2480
2481 int nElems;
2482 if ( !getArraySize( sdoobj->elem_info, nElems ) )
2483 {
2484 qWarning() << "could not determine element info array size";
2485 return false;
2486 }
2487
2488 int nOrds;
2489 if ( !getArraySize( sdoobj->ordinates, nOrds ) )
2490 {
2491 qWarning() << "could not determine ordinate array size";
2492 return false;
2493 }
2494
2495 Q_ASSERT( nElems % 3 == 0 );
2496 Q_ASSERT( nOrds % nDims == 0 );
2497
2498 if ( iType == GtUnknown )
2499 {
2500 qWarning() << "unknown geometry";
2501 return false;
2502 }
2503
2504 if ( iType == GtPoint &&
2505 sdoind->_atomic == OCI_IND_NOTNULL &&
2506 sdoind->point.x == OCI_IND_NOTNULL &&
2507 sdoind->point.y == OCI_IND_NOTNULL )
2508 {
2509 Q_ASSERT( nOrds == 0 );
2510
2511 double x, y, z = 0.0;
2512 if ( !getValue( &sdoobj->point.x, x ) )
2513 {
2514 qWarning() << "could not convert x ordinate to real";
2515 return false;
2516 }
2517
2518 if ( !getValue( &sdoobj->point.y, y ) )
2519 {
2520 qWarning() << "could not convert y ordinate to real";
2521 return false;
2522 }
2523
2524 if ( nDims > 2 )
2525 {
2526 if ( sdoind->point.z != OCI_IND_NOTNULL )
2527 {
2528 qWarning() << "null value in z ordinate";
2529 return false;
2530 }
2531
2532 if ( !getValue( &sdoobj->point.z, z ) )
2533 {
2534 qDebug() << "could not convert z ordinate to real";
2535 return false;
2536 }
2537 }
2538
2539 ba.resize( 1 + sizeof( int ) + nDims * sizeof( double ) );
2540 ptr.cPtr = ba.data();
2541 *ptr.ucPtr++ = byteorder();
2542 *ptr.iPtr++ = nDims == 2 ? WKBPoint : WKBPoint25D;
2543 *ptr.dPtr++ = x;
2544 *ptr.dPtr++ = y;
2545 if ( nDims > 2 )
2546 *ptr.dPtr++ = z;
2547
2548 qDebug() << "returning point";
2549 v = ba;
2550 return true;
2551 }
2552
2553 if ( sdoind->_atomic != OCI_IND_NOTNULL )
2554 {
2555 qWarning() << "geometry with sdo_elem_info and non-null sdo_point found.";
2556 return false;
2557 }
2558
2559 QVector<int> elems( nElems );
2560
2561 try
2562 {
2563 QVector<boolean> exists( nElems );
2564 QVector<OCINumber *> numbers( nElems );
2565 uword nelems = nElems;
2566 OCI_VERIFY_E( d->err, OCICollGetElemArray( d->env, d->err, sdoobj->elem_info, 0, exists.data(), ( void ** ) numbers.data(), nullptr, &nelems ) );
2567 if ( !exists[0] )
2568 {
2569 qWarning() << "element info array does not exists";
2570 throw OCI_ERROR;
2571 }
2572
2573 for ( unsigned int i = 0; i < nelems; i++ )
2574 {
2575 if ( !getValue( numbers[i], elems[i] ) )
2576 {
2577 qWarning() << "get value of element info item" << i << "failed";
2578 throw OCI_ERROR;
2579 }
2580 }
2581 }
2582 catch ( int )
2583 {
2584 return false;
2585 }
2586
2587
2588 QVector<double> ordinates( nOrds );
2589
2590 try
2591 {
2592 QVector<boolean> exists( nOrds );
2593 QVector<OCINumber *> numbers( nOrds );
2594 uword nords = nOrds;
2595 OCI_VERIFY_E( d->err, OCICollGetElemArray( d->env, d->err, sdoobj->ordinates, 0, exists.data(), ( void ** ) numbers.data(), nullptr, &nords ) );
2596 if ( !exists[0] )
2597 {
2598 qWarning() << "ordinate array does not exists";
2599 throw OCI_ERROR;
2600 }
2601 OCI_VERIFY_E( d->err, OCINumberToRealArray( d->err, ( const OCINumber ** ) numbers.data(), nords, sizeof( double ), ordinates.data() ) );
2602 }
2603 catch ( int )
2604 {
2605 return false;
2606 }
2607
2608 if ( iType == GtPoint || iType == GtMultiPoint )
2609 {
2610 int nPoints = 0;
2611
2612 for ( int i = 0; i < nElems; i += 3 )
2613 {
2614 int startOffset, endOffset, etype, n;
2615 if ( !getElemInfoElem( i, elems, nOrds, startOffset, endOffset, etype, n ) )
2616 {
2617 qDebug() << "could not fetch element info" << i;
2618 return false;
2619 }
2620
2621 if ( etype == 1 && n > 0 )
2622 nPoints += endOffset - startOffset;
2623 }
2624
2625 Q_ASSERT( nPoints % nDims == 0 );
2626 Q_ASSERT( iType == GtMultiPoint || nPoints == nDims );
2627
2628 int wkbSize = 0;
2629
2630 if ( nPoints > nDims )
2631 wkbSize += 1 + 2 * sizeof( int );
2632
2633 wkbSize += ( nPoints / nDims ) * ( 1 + sizeof( int ) ) + nPoints * sizeof( double );
2634 qDebug() << "wkbSize" << wkbSize;
2635
2636 ba.resize( wkbSize );
2637 ptr.cPtr = ba.data();
2638
2639 if ( nPoints > nDims )
2640 {
2641 *ptr.ucPtr++ = byteorder();
2642 *ptr.iPtr++ = nDims == 2 ? WKBMultiPoint : WKBMultiPoint25D;
2643 *ptr.iPtr++ = nPoints / nDims;
2644 }
2645
2646 for ( int i = 0; i < nElems; i += 3 )
2647 {
2648 int startOffset, endOffset, etype, n;
2649 if ( !getElemInfoElem( i, elems, nOrds, startOffset, endOffset, etype, n ) )
2650 {
2651 qDebug() << "could not fetch element info" << i;
2652 return false;
2653 }
2654
2655 if ( etype != 1 )
2656 continue;
2657
2658 if ( n == 0 )
2659 {
2660 qDebug() << "point orientation skipped";
2661 continue;
2662 }
2663
2664 Q_ASSERT( ( endOffset - startOffset ) % nDims == 0 );
2665
2666 for ( int j = startOffset, k = 0; j < endOffset; j++, k++ )
2667 {
2668 if ( k % nDims == 0 )
2669 {
2670 *ptr.ucPtr++ = byteorder();
2671 *ptr.iPtr++ = nDims == 2 ? WKBPoint : WKBPoint25D;
2672 }
2673
2674 Q_ASSERT( j < nOrds );
2675 *ptr.dPtr++ = ordinates[ j ];
2676 }
2677 }
2678
2679 qDebug() << "returning (multi)point";
2680 v = ba;
2681 return true;
2682 }
2683
2684 else if ( iType == GtLine || iType == GtMultiLine )
2685 {
2686 Q_ASSERT( nOrds % nDims == 0 );
2687
2688 QVector< QPair<WKBType, CurveParts> > lines;
2689
2690 bool isCurved = false;
2691 for ( int i = 0; i < nElems; i += 3 )
2692 {
2693 bool ok = false;
2694 WKBType baseType = WKBUnknown;
2695 const CurveParts parts = getCurveParts( i, elems, nOrds, ordinates, nDims, baseType, ok );
2696 if ( !ok )
2697 return false;
2698
2699 if ( parts.empty() )
2700 continue;
2701
2702 if ( baseType == WKBCompoundCurve || baseType == WKBCompoundCurveZ ||
2703 baseType == WKBCircularString || baseType == WKBCircularStringZ )
2704 {
2705 isCurved = true;
2706 }
2707 lines << qMakePair( baseType, parts );
2708 }
2709
2710 int binarySize = 1 + sizeof( int ) ;
2711 if ( lines.size() > 1 )
2712 binarySize += sizeof( int );
2713 for ( int partIndex = 0; partIndex < lines.size(); ++partIndex )
2714 {
2715 if ( lines.size() > 1 )
2716 binarySize += 1 + sizeof( int );
2717 auto &line = lines[ partIndex ];
2718
2719 if ( line.first == WKBCompoundCurve || line.first == WKBCompoundCurveZ )
2720 {
2721 binarySize += sizeof( int );
2722 for ( int partNum = 0; partNum < line.second.size() - 1; ++partNum )
2723 {
2724 line.second[ partNum ].second.append( line.second.at( partNum + 1 ).second.first() );
2725 }
2726 }
2727
2728 for ( const CurvePart &part : qAsConst( line.second ) )
2729 {
2730 const PointSequence &pts = part.second;
2731 if ( line.first == WKBCompoundCurve || line.first == WKBCompoundCurveZ )
2732 {
2733 binarySize += 1 + sizeof( int );
2734 }
2735 binarySize += sizeof( int ) + pts.size() * ( nDims ) * sizeof( double );
2736 }
2737 }
2738
2739 ba.resize( binarySize );
2740 ptr.cPtr = ba.data();
2741 *ptr.ucPtr++ = byteorder();
2742
2743 if ( lines.size() == 1 )
2744 {
2745 *ptr.iPtr++ = lines[0].first;
2746 }
2747 else
2748 {
2749 if ( isCurved )
2750 *ptr.iPtr++ = nDims == 2 ? WKBMultiCurve : WKBMultiCurveZ;
2751 else
2752 *ptr.iPtr++ = nDims == 2 ? WKBMultiLineString : WKBMultiLineString25D;
2753 *ptr.iPtr++ = lines.size();
2754 }
2755
2756 for ( const auto &line : qAsConst( lines ) )
2757 {
2758 if ( lines.size() > 1 )
2759 {
2760 *ptr.ucPtr++ = byteorder();
2761 *ptr.iPtr++ = line.first;
2762 }
2763
2764 if ( line.first == WKBCompoundCurve || line.first == WKBCompoundCurveZ )
2765 {
2766 *ptr.iPtr++ = line.second.size();
2767 }
2768 for ( const CurvePart &part : line.second )
2769 {
2770 const PointSequence &pts = part.second;
2771 if ( line.first == WKBCompoundCurve || line.first == WKBCompoundCurveZ )
2772 {
2773 *ptr.ucPtr++ = byteorder();
2774 *ptr.iPtr++ = part.first;
2775 }
2776 *ptr.iPtr++ = pts.size();
2777 for ( const Point &point : pts )
2778 {
2779 *ptr.dPtr++ = point.x;
2780 *ptr.dPtr++ = point.y;
2781 if ( nDims > 2 )
2782 *ptr.dPtr++ = point.z;
2783 }
2784 }
2785 }
2786
2787 Q_ASSERT( ptr.cPtr == ba.data() + ba.size() );
2788
2789 v = ba;
2790 return true;
2791 }
2792
2793 if ( iType == GtPolygon || iType == GtMultiPolygon )
2794 {
2795 QVector< QPair< WKBType, SurfaceRings > > parts;
2796 SurfaceRings currentPart;
2797 WKBType currentPartWkbType = WKBUnknown;
2798
2799 bool isCurved = false;
2800
2801 for ( int i = 0; i < nElems; i += 3 )
2802 {
2803 int startOffset, endOffset, etype, n;
2804 if ( !getElemInfoElem( i, elems, nOrds, startOffset, endOffset, etype, n ) )
2805 {
2806 qWarning() << "could not fetch element info" << i;
2807 return false;
2808 }
2809
2810 if ( etype / 1000 == 1 && !currentPart.empty() )
2811 {
2812 // Exterior ring => new Polygon
2813 parts << qMakePair( currentPartWkbType, currentPart );
2814 currentPart.clear();
2815 currentPartWkbType = WKBUnknown;
2816 }
2817
2818 if ( etype % 1000 == 3 && ( n == 1 || n == 2 ) )
2819 {
2820 // Polygon type or circular arc ring
2821 PointSequence points;
2822 points.reserve( 1 + ( endOffset - startOffset ) / nDims );
2823 for ( int j = startOffset; j < endOffset; j += nDims )
2824 {
2825 double x = ordinates[ j ];
2826 double y = ordinates[ j + 1 ];
2827 double z = nDims > 2 ? ordinates[ j + 2] : 0;
2828 points << Point( x, y, z );
2829 }
2830 WKBType type = WKBUnknown;
2831 if ( n == 1 )
2832 {
2833 // linear ring
2834 type = nDims == 2 ? WKBLineString
2835 : WKBLineString25D;
2836 if ( currentPartWkbType == WKBUnknown )
2837 currentPartWkbType = nDims == 2 ? WKBPolygon : WKBPolygon25D;
2838 }
2839 else if ( n == 2 )
2840 {
2841 // circular arc ring
2842 isCurved = true;
2843 type = nDims == 2 ? WKBCircularString
2844 : WKBCircularStringZ;
2845 currentPartWkbType = nDims == 2 ? WKBCurvePolygon : WKBCurvePolygonZ;
2846 }
2847
2848 currentPart << qMakePair( type, CurveParts() << qMakePair( type, points ) );
2849 }
2850 else if ( etype % 1000 == 3 && n == 3 )
2851 {
2852 // Rectangle - expand to a polygon with 5 points
2853 double x0 = ordinates[ startOffset + 0 ];
2854 double y0 = ordinates[ startOffset + 1 ];
2855 double x1 = ordinates[ startOffset + nDims + 0 ];
2856 double y1 = ordinates[ startOffset + nDims + 1 ];
2857
2858 PointSequence points;
2859 points.reserve( 5 );
2860 points << Point( x0, y0 );
2861 points << Point( x1, y0 );
2862 points << Point( x1, y1 );
2863 points << Point( x0, y1 );
2864 points << Point( x0, y0 );
2865 if ( currentPartWkbType == WKBUnknown )
2866 currentPartWkbType = WKBPolygon;
2867 currentPart << qMakePair( WKBLineString, CurveParts() << qMakePair( WKBLineString, points ) );
2868 }
2869 else if ( etype % 1000 == 3 && n == 4 )
2870 {
2871 // Circle
2872 isCurved = true;
2873 double x0 = ordinates[ startOffset + 0 ];
2874 double y0 = ordinates[ startOffset + 1 ];
2875 double x1 = ordinates[ startOffset + nDims + 0 ];
2876 double y1 = ordinates[ startOffset + nDims + 1 ];
2877 double x2 = ordinates[ startOffset + 2 * nDims + 0 ];
2878 double y2 = ordinates[ startOffset + 2 * nDims + 1 ];
2879 currentPartWkbType = WKBCurvePolygon;
2880 currentPart << qMakePair( WKBCircularString, CurveParts() << qMakePair( WKBCircularString, circlePoints( x0, y0, x1, y1, x2, y2 ) ) );
2881 }
2882 else if ( etype % 1000 == 5 && n > 1 )
2883 {
2884 // CompoundCurve ring
2885 isCurved = true;
2886 int compoundParts = n;
2887 currentPartWkbType = ( nDims == 2 ? WKBCurvePolygon : WKBCurvePolygonZ );
2888 CurveParts parts;
2889 for ( int k = 0; k < compoundParts; k += 1 )
2890 {
2891 i += 3;
2892 if ( !getElemInfoElem( i, elems, nOrds, startOffset, endOffset, etype, n ) )
2893 {
2894 qWarning() << "could not fetch element info" << i;
2895 continue;
2896 }
2897
2898 if ( etype == 2 && ( n == 1 || n == 2 ) )
2899 {
2900 WKBType partType = ( n == 1 ) ?
2901 ( nDims == 2 ? WKBLineString : WKBLineString25D ) :
2902 ( nDims == 2 ? WKBCircularString : WKBCircularStringZ );
2903 PointSequence points;
2904 points.reserve( 1 + ( endOffset - startOffset ) / nDims );
2905 for ( int j = startOffset; j < endOffset; j += nDims )
2906 {
2907 double x = ordinates[ j ];
2908 double y = ordinates[ j + 1 ];
2909 double z = nDims > 2 ? ordinates[ j + 2] : 0;
2910 points << Point( x, y, z );
2911 }
2912 parts << qMakePair( partType, points );
2913 }
2914 else
2915 {
2916 qWarning( "skipped unsupported compound curve element: etype=%08x n=%d", etype, n );
2917 }
2918 }
2919 currentPart << qMakePair( nDims == 2 ? WKBCompoundCurve : WKBCompoundCurveZ, parts );
2920 }
2921 else
2922 {
2923 qWarning( "skipped unsupported polygon element: etype=%08x n=%d", etype, n );
2924 }
2925 }
2926
2927 if ( parts.empty() && currentPart.empty() )
2928 return false;
2929
2930 if ( !currentPart.empty() )
2931 parts << qMakePair( currentPartWkbType, currentPart );
2932
2933 int wkbSize = 1 + sizeof( int );
2934 const int nPolygons = parts.size();
2935 const bool isMultiPolygon = iType == GtMultiPolygon;
2936 if ( isMultiPolygon )
2937 wkbSize += sizeof( int );
2938 for ( int part = 0; part < nPolygons; ++part )
2939 {
2940 SurfaceRings &rings = parts[ part ].second;
2941 if ( isMultiPolygon )
2942 wkbSize += 1 + sizeof( int );
2943 wkbSize += sizeof( int );
2944 for ( int ringIdx = 0; ringIdx < rings.size(); ++ringIdx )
2945 {
2946 CurveParts &ring = rings[ ringIdx ].second;
2947
2948 if ( parts[ part ].first == WKBCurvePolygon || parts[ part ].first == WKBCurvePolygonZ )
2949 {
2950 wkbSize += 1 + sizeof( int );
2951 }
2952 if ( rings[ ringIdx ].first == WKBCompoundCurve || rings[ ringIdx ].first == WKBCompoundCurveZ )
2953 {
2954 wkbSize += sizeof( int );
2955 for ( int partNum = 0; partNum < ring.size() - 1; ++partNum )
2956 {
2957 ring[ partNum ].second.append( ring.at( partNum + 1 ).second.first() );
2958 }
2959 }
2960
2961 for ( const CurvePart &curvePart : qAsConst( ring ) )
2962 {
2963 if ( rings[ ringIdx ].first == WKBCompoundCurve || rings[ ringIdx ].first == WKBCompoundCurveZ )
2964 wkbSize += 1 + sizeof( int );
2965 wkbSize += sizeof( int ) + curvePart.second.size() * nDims * sizeof( double );
2966 }
2967 }
2968 }
2969
2970 qDebug() << "wkbSize" << wkbSize;
2971
2972 ba.resize( wkbSize );
2973
2974 ptr.cPtr = ba.data();
2975 *ptr.ucPtr++ = byteorder();
2976
2977 if ( !isMultiPolygon )
2978 {
2979 if ( isCurved )
2980 *ptr.iPtr++ = nDims == 2 ? WKBCurvePolygon : WKBCurvePolygonZ;
2981 else
2982 *ptr.iPtr++ = nDims == 2 ? WKBPolygon : WKBPolygon25D;
2983 }
2984 else
2985 {
2986 if ( isCurved )
2987 *ptr.iPtr++ = nDims == 2 ? WKBMultiSurface : WKBMultiSurfaceZ;
2988 else
2989 *ptr.iPtr++ = nDims == 2 ? WKBMultiPolygon : WKBMultiPolygon25D;
2990 *ptr.iPtr++ = nPolygons;
2991 }
2992
2993 for ( const QPair< WKBType, SurfaceRings > &rings : qAsConst( parts ) )
2994 {
2995 if ( isMultiPolygon )
2996 {
2997 *ptr.ucPtr++ = byteorder();
2998 *ptr.iPtr++ = rings.first;
2999 }
3000
3001 *ptr.iPtr++ = rings.second.size();
3002 for ( const QPair< WKBType, CurveParts > &ring : rings.second )
3003 {
3004 if ( rings.first == WKBCurvePolygon || rings.first == WKBCurvePolygonZ )
3005 {
3006 *ptr.ucPtr++ = byteorder();
3007 *ptr.iPtr++ = ring.first;
3008 }
3009 if ( ring.first == WKBCompoundCurve || ring.first == WKBCompoundCurveZ )
3010 {
3011 *ptr.iPtr++ = ring.second.size();
3012 }
3013 for ( const CurvePart &curvePart : ring.second )
3014 {
3015 if ( ring.first == WKBCompoundCurve || ring.first == WKBCompoundCurveZ )
3016 {
3017 *ptr.ucPtr++ = byteorder();
3018 *ptr.iPtr++ = curvePart.first;
3019 }
3020
3021 *ptr.iPtr++ = curvePart.second.size();
3022 for ( const Point &point : curvePart.second )
3023 {
3024 *ptr.dPtr++ = point.x;
3025 *ptr.dPtr++ = point.y;
3026 if ( nDims > 2 )
3027 *ptr.dPtr++ = point.z;
3028 }
3029 }
3030 }
3031 }
3032
3033 Q_ASSERT( ptr.cPtr == ba.data() + ba.size() );
3034
3035 qDebug() << "returning (multi)polygon size" << ba.size();
3036 v = ba;
3037 return true;
3038 }
3039
3040 qWarning() << "geometry type" << iType << "not supported";
3041 return false;
3042 }
3043
doubleNear(double a,double b,double epsilon)3044 inline bool doubleNear( double a, double b, double epsilon )
3045 {
3046 const double diff = a - b;
3047 return diff > -epsilon && diff <= epsilon;
3048 }
3049
circlePoints(double x1,double y1,double x2,double y2,double x3,double y3)3050 QOCISpatialCols::PointSequence QOCISpatialCols::circlePoints( double x1, double y1, double x2, double y2, double x3, double y3 )
3051 {
3052 auto isPerpendicular = []( double x1, double y1, double x2, double y2, double x3, double y3 )->bool
3053 {
3054 // check the given point are perpendicular to x or y axis
3055
3056 double yDelta_a = y2 - y1;
3057 double xDelta_a = x2 - x1;
3058 double yDelta_b = y3 - y2;
3059 double xDelta_b = x3 - x2;
3060
3061 if ( ( std::fabs( xDelta_a ) <= 1E-8 ) && ( std::fabs( yDelta_b ) <= 1E-8 ) )
3062 {
3063 return false;
3064 }
3065
3066 if ( std::fabs( yDelta_a ) <= 1E-8 )
3067 {
3068 return true;
3069 }
3070 else if ( std::fabs( yDelta_b ) <= 1E-8 )
3071 {
3072 return true;
3073 }
3074 else if ( std::fabs( xDelta_a ) <= 1E-8 )
3075 {
3076 return true;
3077 }
3078 else if ( std::fabs( xDelta_b ) <= 1E-8 )
3079 {
3080 return true;
3081 }
3082
3083 return false;
3084 };
3085
3086 auto toCircularStringPoints = []( double centerX, double centerY, double radius ) -> PointSequence
3087 {
3088 PointSequence sequence;
3089 sequence.append( Point( centerX, centerY + radius ) );
3090 sequence.append( Point( centerX + radius, centerY ) );
3091 sequence.append( Point( centerX, centerY - radius ) );
3092 sequence.append( Point( centerX - radius, centerY ) );
3093 sequence.append( sequence.at( 0 ) );
3094 return sequence;
3095 };
3096
3097 if ( !isPerpendicular( x1, y1, x2, y2, x3, y3 ) )
3098 {
3099
3100 }
3101 else if ( !isPerpendicular( x1, y1, x3, y3, x2, y2 ) )
3102 {
3103 std::swap( x2, x3 );
3104 std::swap( y2, y3 );
3105 }
3106 else if ( !isPerpendicular( x2, y2, x1, y1, x3, y3 ) )
3107 {
3108 std::swap( x1, x2 );
3109 std::swap( y1, y2 );
3110 }
3111 else if ( !isPerpendicular( x2, y2, x3, y3, x1, y1 ) )
3112 {
3113 double ax1 = x1;
3114 double ay1 = y1;
3115 double ax2 = x2;
3116 double ay2 = y2;
3117 double ax3 = x3;
3118 double ay3 = y3;
3119 x1 = ax2;
3120 y1 = ay2;
3121 x2 = ax3;
3122 y2 = ay3;
3123 x3 = ax1;
3124 y3 = ay1;
3125 }
3126 else if ( !isPerpendicular( x3, y3, x2, y2, x1, y1 ) )
3127 {
3128 std::swap( x1, x3 );
3129 std::swap( y1, y3 );
3130 }
3131 else if ( !isPerpendicular( x2, y3, x1, y1, x2, y2 ) )
3132 {
3133 double ax1 = x1;
3134 double ay1 = y1;
3135 double ax2 = x2;
3136 double ay2 = y2;
3137 double ax3 = x3;
3138 double ay3 = y3;
3139 x1 = ax3;
3140 y1 = ay3;
3141 x2 = ax1;
3142 y2 = ay1;
3143 x3 = ax2;
3144 y3 = ay2;
3145 }
3146 else
3147 {
3148 return PointSequence();
3149 }
3150
3151 double radius = -0.0;
3152 // Paul Bourke's algorithm
3153 double yDelta_a = y2 - y1;
3154 double xDelta_a = x2 - x1;
3155 double yDelta_b = y3 - y2;
3156 double xDelta_b = x3 - x2;
3157
3158 if ( doubleNear( xDelta_a, 0.0, 1E-8 ) || doubleNear( xDelta_b, 0.0, 1E-8 ) )
3159 {
3160 return PointSequence();
3161 }
3162
3163 double aSlope = yDelta_a / xDelta_a;
3164 double bSlope = yDelta_b / xDelta_b;
3165 double centerX = 0;
3166 double centerY = 0;
3167
3168 if ( ( std::fabs( xDelta_a ) <= 1E-8 ) && ( std::fabs( yDelta_b ) <= 1E-8 ) )
3169 {
3170 centerX = ( 0.5 * ( x2 + x3 ) );
3171 centerY = ( 0.5 * ( y1 + y2 ) );
3172 radius = std::sqrt( ( centerX - x1 ) * ( centerX - x1 ) + ( centerY - y1 ) * ( centerY - y1 ) );
3173 return toCircularStringPoints( centerX, centerY, radius );
3174 }
3175
3176 if ( std::fabs( aSlope - bSlope ) <= 1E-8 )
3177 {
3178 return PointSequence();
3179 }
3180
3181 centerX = (
3182 ( aSlope * bSlope * ( y1 - y3 ) +
3183 bSlope * ( x1 + x2 ) -
3184 aSlope * ( x2 + x3 ) ) /
3185 ( 2.0 * ( bSlope - aSlope ) )
3186 );
3187 centerY = (
3188 -1.0 * ( centerX - ( x1 + x2 ) / 2.0 ) /
3189 aSlope + ( y1 + y2 ) / 2.0
3190 );
3191
3192 radius = std::sqrt( ( centerX - x1 ) * ( centerX - x1 ) + ( centerY - y1 ) * ( centerY - y1 ) );
3193 return toCircularStringPoints( centerX, centerY, radius );
3194 }
3195
getValues(QVector<QVariant> & v,int index)3196 void QOCISpatialCols::getValues( QVector<QVariant> &v, int index )
3197 {
3198 ENTER
3199 for ( int i = 0, gcindex = 0; i < fieldInf.size(); ++i )
3200 {
3201 qDebug() << "getValues( index =" << index << "i =" << i << " )";
3202 const OraFieldInf &fld = fieldInf.at( i );
3203
3204 if ( fld.ind == -1 )
3205 {
3206 // got a NULL value
3207 qDebug() << "NULL";
3208 v[index + i] = QVariant( fld.typ );
3209 continue;
3210 }
3211
3212 qDebug() << "oraType:" << fld.oraType;
3213 if ( fld.oraType == SQLT_BIN || fld.oraType == SQLT_LBI || fld.oraType == SQLT_LNG )
3214 {
3215 qDebug() << "fetching piecewise";
3216 continue; // already fetched piecewise
3217 }
3218
3219 switch ( fld.typ )
3220 {
3221 case QVariant::DateTime:
3222 qDebug() << "DateTime";
3223 v[index + i] = QVariant( qMakeDate( fld.data ) );
3224 break;
3225 case QVariant::Double:
3226 case QVariant::Int:
3227 case QVariant::LongLong:
3228 if ( d->q_func()->numericalPrecisionPolicy() != QSql::HighPrecision )
3229 {
3230 if ( ( d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionDouble )
3231 && ( fld.typ == QVariant::Double ) )
3232 {
3233 v[index + i] = *reinterpret_cast<double *>( fld.data );
3234 qDebug() << "double" << v[index + i].toDouble();
3235 break;
3236 }
3237 else if ( ( d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt64 )
3238 && ( fld.typ == QVariant::LongLong ) )
3239 {
3240 qint64 qll = 0;
3241 int r = OCINumberToInt( d->err, reinterpret_cast<OCINumber *>( fld.data ), sizeof( qint64 ),
3242 OCI_NUMBER_SIGNED, &qll );
3243 if ( r == OCI_SUCCESS )
3244 {
3245 v[index + i] = qll;
3246 qDebug() << "qint64" << qll;
3247 }
3248 else
3249 {
3250 qDebug() << "qint64 invalid";
3251 v[index + i] = QVariant();
3252 }
3253 break;
3254 }
3255 else if ( ( d->q_func()->numericalPrecisionPolicy() == QSql::LowPrecisionInt32 )
3256 && ( fld.typ == QVariant::Int ) )
3257 {
3258 v[index + i] = *reinterpret_cast<int *>( fld.data );
3259 qDebug() << "int" << v[index + i].toInt();
3260 break;
3261 }
3262 }
3263
3264 FALLTHROUGH
3265
3266 case QVariant::String:
3267 qDebug() << "String";
3268 v[index + i] = QString( reinterpret_cast<const QChar *>( fld.data ) );
3269 qDebug() << "string" << v[index + i].toString();
3270 break;
3271 case QVariant::ByteArray:
3272 if ( fld.oraType == SQLT_NTY && fld.oraTypeName == "SDO_GEOMETRY" )
3273 {
3274 qDebug() << "SQLT_NTY SDO_GEOMETRY";
3275 convertToWkb( v[ index + i ], gcindex++ );
3276 }
3277 else
3278 {
3279 qDebug() << "ByteArray length =" << fld.len;
3280 if ( fld.len > 0 )
3281 v[index + i] = QByteArray( fld.data, fld.len );
3282 else
3283 v[index + i] = QVariant( QVariant::ByteArray );
3284 }
3285 break;
3286 default:
3287 qWarning( "QOCISpatialCols::value: unknown data type %s", QVariant::typeToName( fld.typ ) );
3288 break;
3289 }
3290 }
3291 }
3292
QOCISpatialResultPrivate(QOCISpatialResult * q,const QOCISpatialDriver * drv)3293 QOCISpatialResultPrivate::QOCISpatialResultPrivate( QOCISpatialResult *q, const QOCISpatialDriver *drv )
3294 : QSqlCachedResultPrivate( q, drv )
3295 , env( drv_d_func()->env )
3296 , svc( const_cast<OCISvcCtx * &>( drv_d_func()->svc ) )
3297 , transaction( drv_d_func()->transaction )
3298 , commitOnSuccess( drv_d_func()->commitOnSuccess )
3299 , serverVersion( drv_d_func()->serverVersion )
3300 , prefetchRows( drv_d_func()->prefetchRows )
3301 , prefetchMem( drv_d_func()->prefetchMem )
3302 , geometryTDO( drv_d_func()->geometryTDO )
3303 {
3304 ENTER
3305 int r = OCIHandleAlloc( env,
3306 reinterpret_cast<void **>( &err ),
3307 OCI_HTYPE_ERROR,
3308 0,
3309 nullptr );
3310 if ( r != OCI_SUCCESS )
3311 qWarning( "QOCISpatialResult: unable to alloc error handle" );
3312 }
3313
~QOCISpatialResultPrivate()3314 QOCISpatialResultPrivate::~QOCISpatialResultPrivate()
3315 {
3316 ENTER
3317 delete cols;
3318
3319 int r;
3320 if ( geometryObj )
3321 {
3322 r = OCIObjectFree( env, err, geometryObj, OCI_OBJECTFREE_FORCE );
3323 if ( r != OCI_SUCCESS )
3324 qOraWarning( "~QOCISpatialResult: unable to free geometry object", err );
3325 }
3326
3327 r = OCIHandleFree( err, OCI_HTYPE_ERROR );
3328 if ( r != OCI_SUCCESS )
3329 qWarning( "~QOCISpatialResult: unable to free error handle" );
3330
3331 if ( sql )
3332 {
3333 r = OCIHandleFree( sql, OCI_HTYPE_STMT );
3334 if ( r != OCI_SUCCESS )
3335 qWarning( "~QOCISpatialResult: unable to free statement handle" );
3336 }
3337 }
3338
3339
3340 ////////////////////////////////////////////////////////////////////////////
3341
QOCISpatialResult(const QOCISpatialDriver * db)3342 QOCISpatialResult::QOCISpatialResult( const QOCISpatialDriver *db )
3343 : QSqlCachedResult( *new QOCISpatialResultPrivate( this, db ) )
3344 {
3345 ENTER
3346 }
3347
~QOCISpatialResult()3348 QOCISpatialResult::~QOCISpatialResult()
3349 {
3350 ENTER
3351 }
3352
handle() const3353 QVariant QOCISpatialResult::handle() const
3354 {
3355 ENTER
3356 return QVariant::fromValue( d_func()->sql );
3357 }
3358
reset(const QString & query)3359 bool QOCISpatialResult::reset( const QString &query )
3360 {
3361 ENTER
3362 return prepare( query ) && exec();
3363 }
3364
gotoNext(QSqlCachedResult::ValueCache & values,int index)3365 bool QOCISpatialResult::gotoNext( QSqlCachedResult::ValueCache &values, int index )
3366 {
3367 ENTER
3368 Q_D( QOCISpatialResult );
3369 qDebug() << "gotoNext( index =" << index << ")";
3370 if ( at() == QSql::AfterLastRow )
3371 return false;
3372
3373 bool piecewise = false;
3374 int r = OCIStmtFetch2( d->sql, d->err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT );
3375
3376 if ( index < 0 ) //not interested in values
3377 return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
3378
3379 switch ( r )
3380 {
3381 case OCI_SUCCESS:
3382 break;
3383 case OCI_SUCCESS_WITH_INFO:
3384 qOraWarning( "QOCISpatialResult::gotoNext: SuccessWithInfo: ", d->err );
3385 qDebug() << "QOCISpatialResult::gotoNext: statement " << lastQuery();
3386 r = OCI_SUCCESS; //ignore it
3387 break;
3388 case OCI_NO_DATA:
3389 // end of rowset
3390 return false;
3391 case OCI_NEED_DATA:
3392 qDebug( "QOCISpatialResult::gotoNext: NEED DATA - fetching piecewise" );
3393 piecewise = true;
3394 r = OCI_SUCCESS;
3395 break;
3396 case OCI_ERROR:
3397 if ( qOraErrorNumber( d->err ) == 1406 )
3398 {
3399 qWarning( "QOCISpatial Warning: data truncated for %s", lastQuery().toLocal8Bit().constData() );
3400 r = OCI_SUCCESS; /* ignore it */
3401 break;
3402 }
3403 FALLTHROUGH
3404
3405 default:
3406 qOraWarning( "QOCISpatialResult::gotoNext: ", d->err );
3407 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
3408 "Unable to goto next" ),
3409 QSqlError::StatementError, d->err ) );
3410 break;
3411 }
3412
3413 // need to read piecewise before assigning values
3414 if ( r == OCI_SUCCESS && piecewise )
3415 {
3416 values.clear();
3417 r = d->cols->readPiecewise( values, index );
3418 }
3419
3420 if ( r == OCI_SUCCESS )
3421 d->cols->getValues( values, index );
3422
3423 if ( r == OCI_SUCCESS )
3424 r = d->cols->readLOBs( values, index );
3425
3426 if ( r != OCI_SUCCESS )
3427 setAt( QSql::AfterLastRow );
3428
3429 return r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO;
3430 }
3431
size()3432 int QOCISpatialResult::size()
3433 {
3434 ENTER
3435 Q_D( QOCISpatialResult );
3436 int rowCount;
3437 if ( OCIAttrGet( d->sql,
3438 OCI_HTYPE_STMT,
3439 &rowCount,
3440 nullptr,
3441 OCI_ATTR_ROWS_FETCHED,
3442 d->err ) == OCI_SUCCESS )
3443 {
3444 return rowCount;
3445 }
3446 else
3447 {
3448 return -1;
3449 }
3450 }
3451
numRowsAffected()3452 int QOCISpatialResult::numRowsAffected()
3453 {
3454 ENTER
3455 Q_D( QOCISpatialResult );
3456 int rowCount;
3457 OCIAttrGet( d->sql,
3458 OCI_HTYPE_STMT,
3459 &rowCount,
3460 nullptr,
3461 OCI_ATTR_ROW_COUNT,
3462 d->err );
3463 return rowCount;
3464 }
3465
prepare(const QString & query)3466 bool QOCISpatialResult::prepare( const QString &query )
3467 {
3468 ENTER
3469 Q_D( QOCISpatialResult );
3470
3471 static int sDebugLevel = -1;
3472 if ( sDebugLevel < 0 )
3473 {
3474 if ( getenv( "QGIS_DEBUG" ) )
3475 sDebugLevel = atoi( getenv( "QGIS_DEBUG" ) );
3476 else
3477 sDebugLevel = 0;
3478 }
3479
3480 if ( sDebugLevel >= 4 )
3481 qDebug() << "prepare(" << query << ")";
3482
3483 int r = 0;
3484 QSqlResult::prepare( query );
3485
3486 delete d->cols;
3487 d->cols = nullptr;
3488 QSqlCachedResult::cleanup();
3489
3490 if ( d->sql )
3491 {
3492 r = OCIHandleFree( d->sql, OCI_HTYPE_STMT );
3493 if ( r != OCI_SUCCESS )
3494 qOraWarning( "QOCISpatialResult::prepare: unable to free statement handle:", d->err );
3495 d->sql = nullptr;
3496 }
3497 if ( query.isEmpty() )
3498 return false;
3499 r = OCIHandleAlloc( d->env,
3500 reinterpret_cast<void **>( &d->sql ),
3501 OCI_HTYPE_STMT,
3502 0,
3503 nullptr );
3504 if ( r != OCI_SUCCESS )
3505 {
3506 qOraWarning( "QOCISpatialResult::prepare: unable to alloc statement:", d->err );
3507 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
3508 "Unable to alloc statement" ), QSqlError::StatementError, d->err ) );
3509 return false;
3510 }
3511 d->setStatementAttributes();
3512 const OraText *txt = reinterpret_cast<const OraText *>( query.utf16() );
3513 const int len = query.length() * sizeof( QChar );
3514 r = OCIStmtPrepare( d->sql,
3515 d->err,
3516 txt,
3517 len,
3518 OCI_NTV_SYNTAX,
3519 OCI_DEFAULT );
3520 if ( r != OCI_SUCCESS )
3521 {
3522 qOraWarning( "QOCISpatialResult::prepare: unable to prepare statement:", d->err );
3523 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
3524 "Unable to prepare statement" ), QSqlError::StatementError, d->err ) );
3525 return false;
3526 }
3527
3528 return true;
3529 }
3530
exec()3531 bool QOCISpatialResult::exec()
3532 {
3533 ENTER
3534 Q_D( QOCISpatialResult );
3535 int r = 0;
3536 ub2 stmtType = 0;
3537 ub4 iters;
3538 ub4 mode;
3539 QList<QByteArray> tmpStorage;
3540 IndicatorArray indicators( boundValueCount() );
3541 SizeArray tmpSizes( boundValueCount() );
3542
3543 r = OCIAttrGet( d->sql,
3544 OCI_HTYPE_STMT,
3545 &stmtType,
3546 nullptr,
3547 OCI_ATTR_STMT_TYPE,
3548 d->err );
3549
3550 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
3551 {
3552 qOraWarning( "QOCISpatialResult::exec: Unable to get statement type:", d->err );
3553 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
3554 "Unable to get statement type" ), QSqlError::StatementError, d->err ) );
3555 qWarning( "type retrieval failed with statement:%s", lastQuery().toLocal8Bit().constData() );
3556 return false;
3557 }
3558
3559 iters = stmtType == OCI_STMT_SELECT ? 0 : 1;
3560 mode = d->transaction || !d->commitOnSuccess ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
3561
3562 qDebug() << "iters:" << iters;
3563
3564 // bind placeholders
3565 if ( boundValueCount() > 0
3566 && d->bindValues( boundValues(), indicators, tmpSizes, tmpStorage ) != OCI_SUCCESS )
3567 {
3568 qOraWarning( "QOCISpatialResult::exec: unable to bind value: ", d->err );
3569 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult", "Unable to bind value" ),
3570 QSqlError::StatementError, d->err ) );
3571 qWarning( "bind failed with statement:%s", lastQuery().toLocal8Bit().constData() );
3572 return false;
3573 }
3574
3575 // execute
3576 r = OCIStmtExecute( d->svc,
3577 d->sql,
3578 d->err,
3579 iters,
3580 0,
3581 nullptr,
3582 nullptr,
3583 mode );
3584 if ( r != OCI_SUCCESS && r != OCI_SUCCESS_WITH_INFO )
3585 {
3586 qOraWarning( "QOCISpatialResult::exec: unable to execute statement:", d->err );
3587 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialResult",
3588 "Unable to execute statement" ), QSqlError::StatementError, d->err ) );
3589 qWarning( "execution failed with statement:%s", lastQuery().toLocal8Bit().constData() );
3590 return false;
3591 }
3592
3593 if ( stmtType == OCI_STMT_SELECT )
3594 {
3595 ub4 parmCount = 0;
3596 int r = OCIAttrGet( d->sql, OCI_HTYPE_STMT, reinterpret_cast<void **>( &parmCount ),
3597 nullptr, OCI_ATTR_PARAM_COUNT, d->err );
3598 if ( r == OCI_SUCCESS && !d->cols )
3599 {
3600 d->sdoobj.clear();
3601 d->sdoind.clear();
3602 d->cols = new QOCISpatialCols( parmCount, d );
3603 }
3604 else
3605 qOraWarning( "QOCISpatialResult::exec: get param count failed:", d->err );
3606 setSelect( true );
3607 QSqlCachedResult::init( parmCount );
3608 }
3609 else /* non-SELECT */
3610 {
3611 setSelect( false );
3612 }
3613 setAt( QSql::BeforeFirstRow );
3614 setActive( true );
3615
3616 if ( hasOutValues() )
3617 d->outValues( boundValues(), indicators, tmpStorage );
3618
3619 return true;
3620 }
3621
record() const3622 QSqlRecord QOCISpatialResult::record() const
3623 {
3624 ENTER
3625 Q_D( const QOCISpatialResult );
3626 QSqlRecord inf;
3627 if ( !isActive() || !isSelect() || !d->cols )
3628 return inf;
3629 return d->cols->rec;
3630 }
3631
lastInsertId() const3632 QVariant QOCISpatialResult::lastInsertId() const
3633 {
3634 ENTER
3635 Q_D( const QOCISpatialResult );
3636 if ( isActive() )
3637 {
3638 QOCISpatialRowIdPointer ptr( new QOCISpatialRowId( d->env ) );
3639
3640 int r = OCIAttrGet( d->sql, OCI_HTYPE_STMT, ptr.constData()->id,
3641 nullptr, OCI_ATTR_ROWID, d->err );
3642 if ( r == OCI_SUCCESS )
3643 return QVariant::fromValue( ptr );
3644 }
3645 return QVariant();
3646 }
3647
execBatch(bool arrayBind)3648 bool QOCISpatialResult::execBatch( bool arrayBind )
3649 {
3650 Q_D( QOCISpatialResult );
3651 QOCISpatialCols::execBatch( d, boundValues(), arrayBind );
3652 resetBindCount();
3653 return lastError().type() == QSqlError::NoError;
3654 }
3655
virtual_hook(int id,void * data)3656 void QOCISpatialResult::virtual_hook( int id, void *data )
3657 {
3658 Q_ASSERT( data );
3659
3660 QSqlCachedResult::virtual_hook( id, data );
3661 }
3662
3663 ////////////////////////////////////////////////////////////////////////////
3664
3665
QOCISpatialDriver(QObject * parent)3666 QOCISpatialDriver::QOCISpatialDriver( QObject *parent )
3667 : QSqlDriver( *new QOCISpatialDriverPrivate, parent )
3668 {
3669 ENTER
3670 Q_D( QOCISpatialDriver );
3671
3672 #ifdef QOCISPATIAL_THREADED
3673 const ub4 mode = OCI_UTF16 | OCI_OBJECT | OCI_THREADED;
3674 #else
3675 const ub4 mode = OCI_UTF16 | OCI_OBJECT;
3676 #endif
3677 int r = OCIEnvCreate( &d->env,
3678 mode,
3679 nullptr,
3680 nullptr,
3681 nullptr,
3682 nullptr,
3683 0,
3684 nullptr );
3685 if ( r != OCI_SUCCESS )
3686 {
3687 qWarning( "QOCISpatialDriver: unable to create environment" );
3688 setLastError( qMakeError( tr( "Unable to initialize", "QOCISpatialDriver" ),
3689 QSqlError::ConnectionError, d->err ) );
3690 return;
3691 }
3692
3693 d->allocErrorHandle();
3694 }
3695
QOCISpatialDriver(OCIEnv * env,OCISvcCtx * ctx,QObject * parent)3696 QOCISpatialDriver::QOCISpatialDriver( OCIEnv *env, OCISvcCtx *ctx, QObject *parent )
3697 : QSqlDriver( *new QOCISpatialDriverPrivate, parent )
3698 {
3699 ENTER
3700 Q_D( QOCISpatialDriver );
3701 d->env = env;
3702 d->svc = ctx;
3703
3704 d->allocErrorHandle();
3705
3706 if ( env && ctx )
3707 {
3708 setOpen( true );
3709 setOpenError( false );
3710 }
3711 }
3712
~QOCISpatialDriver()3713 QOCISpatialDriver::~QOCISpatialDriver()
3714 {
3715 ENTER
3716 Q_D( QOCISpatialDriver );
3717 if ( isOpen() )
3718 close();
3719 int r = OCIHandleFree( d->err, OCI_HTYPE_ERROR );
3720 if ( r != OCI_SUCCESS )
3721 qWarning( "Unable to free Error handle: %d", r );
3722 r = OCIHandleFree( d->env, OCI_HTYPE_ENV );
3723 if ( r != OCI_SUCCESS )
3724 qWarning( "Unable to free Environment handle: %d", r );
3725
3726 // delete d;
3727 }
3728
hasFeature(DriverFeature f) const3729 bool QOCISpatialDriver::hasFeature( DriverFeature f ) const
3730 {
3731 ENTER
3732 Q_D( const QOCISpatialDriver );
3733 switch ( f )
3734 {
3735 case Transactions:
3736 case LastInsertId:
3737 case BLOB:
3738 case PreparedQueries:
3739 case NamedPlaceholders:
3740 case BatchOperations:
3741 case LowPrecisionNumbers:
3742 return true;
3743 case QuerySize:
3744 case PositionalPlaceholders:
3745 case SimpleLocking:
3746 case EventNotifications:
3747 case FinishQuery:
3748 case CancelQuery:
3749 case MultipleResultSets:
3750 return false;
3751 case Unicode:
3752 return d->serverVersion >= 9;
3753 }
3754 return false;
3755 }
3756
qParseOpts(const QString & options,QOCISpatialDriverPrivate * d)3757 static void qParseOpts( const QString &options, QOCISpatialDriverPrivate *d )
3758 {
3759 ENTER
3760 const QStringList opts( options.split( QLatin1Char( ';' ), QString::SkipEmptyParts ) );
3761 for ( int i = 0; i < opts.count(); ++i )
3762 {
3763 const QString tmp( opts.at( i ) );
3764 int idx;
3765 if ( ( idx = tmp.indexOf( QLatin1Char( '=' ) ) ) == -1 )
3766 {
3767 qWarning( "QOCISpatialDriver::parseArgs: Invalid parameter: '%s'",
3768 tmp.toLocal8Bit().constData() );
3769 continue;
3770 }
3771 const QString opt = tmp.left( idx );
3772 const QString val = tmp.mid( idx + 1 ).simplified();
3773 bool ok;
3774 if ( opt == QLatin1String( "OCI_ATTR_PREFETCH_ROWS" ) )
3775 {
3776 int intVal = val.toInt( &ok );
3777 if ( !ok )
3778 d->prefetchRows = QOCISPATIAL_PREFETCH_ROWS;
3779 else if ( intVal >= 0 )
3780 d->prefetchRows = static_cast<ub4>( intVal );
3781 }
3782 else if ( opt == QLatin1String( "OCI_ATTR_PREFETCH_MEMORY" ) )
3783 {
3784 int intVal = val.toInt( &ok );
3785 if ( !ok )
3786 d->prefetchMem = QOCISPATIAL_PREFETCH_MEM;
3787 else if ( intVal >= 0 )
3788 d->prefetchMem = static_cast<ub4>( intVal );
3789 }
3790 else if ( opt == QLatin1String( "COMMIT_ON_SUCCESS" ) )
3791 {
3792 d->commitOnSuccess = val.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3793 }
3794 else
3795 {
3796 qWarning( "QOCISpatialDriver::parseArgs: Invalid parameter: '%s'",
3797 opt.toLocal8Bit().constData() );
3798 }
3799 }
3800 }
3801
open(const QString & db,const QString & user,const QString & password,const QString & hostname,int port,const QString & opts)3802 bool QOCISpatialDriver::open( const QString &db,
3803 const QString &user,
3804 const QString &password,
3805 const QString &hostname,
3806 int port,
3807 const QString &opts )
3808 {
3809 ENTER
3810 Q_D( QOCISpatialDriver );
3811 int r;
3812
3813 if ( isOpen() )
3814 close();
3815
3816 qParseOpts( opts, d );
3817
3818 // Connect without tnsnames.ora if a hostname is given
3819 QString connectionString = db;
3820 if ( !hostname.isEmpty() )
3821 connectionString =
3822 QString::fromLatin1( "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
3823 "(CONNECT_DATA=(SID=%3)))" ).arg( hostname ).arg( ( port > -1 ? port : 1521 ) ).arg( db );
3824
3825 r = OCIHandleAlloc( d->env, reinterpret_cast<void **>( &d->srvhp ), OCI_HTYPE_SERVER, 0, nullptr );
3826 if ( r == OCI_SUCCESS )
3827 r = OCIServerAttach( d->srvhp, d->err, reinterpret_cast<const OraText *>( connectionString.utf16() ),
3828 connectionString.length() * sizeof( QChar ), OCI_DEFAULT );
3829 if ( r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO )
3830 r = OCIHandleAlloc( d->env, reinterpret_cast<void **>( &d->svc ), OCI_HTYPE_SVCCTX, 0, nullptr );
3831 if ( r == OCI_SUCCESS )
3832 r = OCIAttrSet( d->svc, OCI_HTYPE_SVCCTX, d->srvhp, 0, OCI_ATTR_SERVER, d->err );
3833 if ( r == OCI_SUCCESS )
3834 r = OCIHandleAlloc( d->env, reinterpret_cast<void **>( &d->authp ), OCI_HTYPE_SESSION, 0, nullptr );
3835 if ( r == OCI_SUCCESS )
3836 r = OCIAttrSet( d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>( user.utf16() ),
3837 user.length() * sizeof( QChar ), OCI_ATTR_USERNAME, d->err );
3838 if ( r == OCI_SUCCESS )
3839 r = OCIAttrSet( d->authp, OCI_HTYPE_SESSION, const_cast<ushort *>( password.utf16() ),
3840 password.length() * sizeof( QChar ), OCI_ATTR_PASSWORD, d->err );
3841
3842 OCITrans *trans = nullptr;
3843 if ( r == OCI_SUCCESS )
3844 r = OCIHandleAlloc( d->env, reinterpret_cast<void **>( &trans ), OCI_HTYPE_TRANS, 0, nullptr );
3845 if ( r == OCI_SUCCESS )
3846 r = OCIAttrSet( d->svc, OCI_HTYPE_SVCCTX, trans, 0, OCI_ATTR_TRANS, d->err );
3847
3848 if ( r == OCI_SUCCESS )
3849 {
3850 if ( user.isEmpty() && password.isEmpty() )
3851 r = OCISessionBegin( d->svc, d->err, d->authp, OCI_CRED_EXT, OCI_DEFAULT );
3852 else
3853 r = OCISessionBegin( d->svc, d->err, d->authp, OCI_CRED_RDBMS, OCI_DEFAULT );
3854 }
3855 if ( r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO )
3856 r = OCIAttrSet( d->svc, OCI_HTYPE_SVCCTX, d->authp, 0, OCI_ATTR_SESSION, d->err );
3857
3858 if ( r == OCI_SUCCESS || r == OCI_SUCCESS_WITH_INFO )
3859 {
3860 d->geometryTDO = d->tdo( "MDSYS.SDO_GEOMETRY" );
3861 if ( !d->geometryTDO )
3862 {
3863 qDebug() << "MDSYS.SDO_GEOMETRY TDO not found";
3864 r = OCI_INVALID_HANDLE;
3865 }
3866 }
3867
3868 if ( r != OCI_SUCCESS )
3869 {
3870 setLastError( qMakeError( tr( "Unable to logon" ), QSqlError::ConnectionError, d->err ) );
3871 setOpenError( true );
3872 if ( d->authp )
3873 {
3874 OCISessionEnd( d->svc, d->err, d->authp, OCI_DEFAULT );
3875 OCIHandleFree( d->authp, OCI_HTYPE_SESSION );
3876 }
3877 d->authp = nullptr;
3878 if ( d->srvhp )
3879 {
3880 OCIServerDetach( d->srvhp, d->err, OCI_DEFAULT );
3881 OCIHandleFree( d->srvhp, OCI_HTYPE_SERVER );
3882 }
3883 d->srvhp = nullptr;
3884 return false;
3885 }
3886
3887 // get server version
3888 char vertxt[512];
3889 r = OCIServerVersion( d->svc,
3890 d->err,
3891 reinterpret_cast<OraText *>( vertxt ),
3892 sizeof( vertxt ),
3893 OCI_HTYPE_SVCCTX );
3894 if ( r != OCI_SUCCESS )
3895 {
3896 qWarning( "QOCISpatialDriver::open: could not get Oracle server version." );
3897 }
3898 else
3899 {
3900 QString versionStr;
3901 versionStr = QString( reinterpret_cast<const QChar *>( vertxt ) );
3902 QRegExp vers( QLatin1String( "([0-9]+)\\.[0-9\\.]+[0-9]" ) );
3903 if ( vers.indexIn( versionStr ) >= 0 )
3904 d->serverVersion = vers.cap( 1 ).toInt();
3905 if ( d->serverVersion == 0 )
3906 d->serverVersion = -1;
3907 }
3908
3909 setOpen( true );
3910 setOpenError( false );
3911 d->user = user;
3912
3913 return true;
3914 }
3915
close()3916 void QOCISpatialDriver::close()
3917 {
3918 ENTER
3919 Q_D( QOCISpatialDriver );
3920 if ( !isOpen() )
3921 return;
3922
3923 OCISessionEnd( d->svc, d->err, d->authp, OCI_DEFAULT );
3924 OCIServerDetach( d->srvhp, d->err, OCI_DEFAULT );
3925 OCIHandleFree( d->authp, OCI_HTYPE_SESSION );
3926 d->authp = nullptr;
3927 OCIHandleFree( d->srvhp, OCI_HTYPE_SERVER );
3928 d->srvhp = nullptr;
3929 OCIHandleFree( d->svc, OCI_HTYPE_SVCCTX );
3930 d->svc = nullptr;
3931 setOpen( false );
3932 setOpenError( false );
3933 }
3934
createResult() const3935 QSqlResult *QOCISpatialDriver::createResult() const
3936 {
3937 ENTER
3938 return new QOCISpatialResult( this );
3939 }
3940
beginTransaction()3941 bool QOCISpatialDriver::beginTransaction()
3942 {
3943 ENTER
3944 Q_D( QOCISpatialDriver );
3945 if ( !isOpen() )
3946 {
3947 qWarning( "QOCISpatialDriver::beginTransaction: Database not open" );
3948 return false;
3949 }
3950 int r = OCITransStart( d->svc,
3951 d->err,
3952 2,
3953 OCI_TRANS_READWRITE );
3954 if ( r == OCI_ERROR )
3955 {
3956 qOraWarning( "QOCISpatialDriver::beginTransaction: ", d->err );
3957 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialDriver",
3958 "Unable to begin transaction" ), QSqlError::TransactionError, d->err ) );
3959 return false;
3960 }
3961 d->transaction = true;
3962 return true;
3963 }
3964
commitTransaction()3965 bool QOCISpatialDriver::commitTransaction()
3966 {
3967 ENTER
3968 Q_D( QOCISpatialDriver );
3969 if ( !isOpen() )
3970 {
3971 qWarning( "QOCISpatialDriver::commitTransaction: Database not open" );
3972 return false;
3973 }
3974 int r = OCITransCommit( d->svc,
3975 d->err,
3976 0 );
3977 if ( r == OCI_ERROR )
3978 {
3979 qOraWarning( "QOCISpatialDriver::commitTransaction:", d->err );
3980 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialDriver",
3981 "Unable to commit transaction" ), QSqlError::TransactionError, d->err ) );
3982 return false;
3983 }
3984 d->transaction = false;
3985 return true;
3986 }
3987
rollbackTransaction()3988 bool QOCISpatialDriver::rollbackTransaction()
3989 {
3990 ENTER
3991 Q_D( QOCISpatialDriver );
3992 if ( !isOpen() )
3993 {
3994 qWarning( "QOCISpatialDriver::rollbackTransaction: Database not open" );
3995 return false;
3996 }
3997 int r = OCITransRollback( d->svc,
3998 d->err,
3999 0 );
4000 if ( r == OCI_ERROR )
4001 {
4002 qOraWarning( "QOCISpatialDriver::rollbackTransaction:", d->err );
4003 setLastError( qMakeError( QCoreApplication::translate( "QOCISpatialDriver",
4004 "Unable to rollback transaction" ), QSqlError::TransactionError, d->err ) );
4005 return false;
4006 }
4007 d->transaction = false;
4008 return true;
4009 }
4010
4011 enum Expression
4012 {
4013 OrExpression,
4014 AndExpression
4015 };
4016
make_where_clause(const QString & user,Expression e)4017 static QString make_where_clause( const QString &user, Expression e )
4018 {
4019 static const char sysUsers[][8] =
4020 {
4021 "MDSYS",
4022 "LBACSYS",
4023 "SYS",
4024 "SYSTEM",
4025 "WKSYS",
4026 "CTXSYS",
4027 "WMSYS",
4028 };
4029 static const char joinC[][4] = { "or", "and" };
4030 static Q_CONSTEXPR QLatin1Char bang[] = { QLatin1Char( ' ' ), QLatin1Char( '!' ) };
4031
4032 const QLatin1String join( joinC[e], -1 ); // -1: force strlen call
4033
4034 QString result;
4035 result.reserve( sizeof sysUsers / sizeof * sysUsers *
4036 // max-sizeof(owner != <sysuser> and )
4037 ( 9 + sizeof * sysUsers + 5 ) );
4038 for ( const auto &sysUser : sysUsers )
4039 {
4040 const QLatin1String l1( sysUser, -1 ); // -1: force strlen call
4041 if ( l1 != user )
4042 result += QLatin1String( "owner " ) + bang[e] + QLatin1String( "= '" ) + l1 + QLatin1Char( ' ' ) + join + QLatin1Char( ' ' );
4043 }
4044
4045 result.chop( join.size() + 2 ); // remove final " <join> "
4046
4047 return result;
4048 }
4049
tables(QSql::TableType type) const4050 QStringList QOCISpatialDriver::tables( QSql::TableType type ) const
4051 {
4052 ENTER
4053 Q_D( const QOCISpatialDriver );
4054 QStringList tl;
4055
4056 QString user = d->user;
4057 if ( isIdentifierEscaped( user, QSqlDriver::TableName ) )
4058 user = stripDelimiters( user, QSqlDriver::TableName );
4059 else
4060 user = user.toUpper();
4061
4062 if ( !isOpen() )
4063 return tl;
4064
4065 QSqlQuery t( createResult() );
4066 t.setForwardOnly( true );
4067 if ( type & QSql::Tables )
4068 {
4069 const QLatin1String tableQuery( "select owner, table_name from all_tables where " );
4070 const QString where = make_where_clause( user, AndExpression );
4071 t.exec( tableQuery + where );
4072 while ( t.next() )
4073 {
4074 if ( t.value( 0 ).toString().toUpper() != user.toUpper() )
4075 tl.append( t.value( 0 ).toString() + QLatin1Char( '.' ) + t.value( 1 ).toString() );
4076 else
4077 tl.append( t.value( 1 ).toString() );
4078 }
4079
4080 // list all table synonyms as well
4081 const QLatin1String synonymQuery( "select owner, synonym_name from all_synonyms where " );
4082 t.exec( synonymQuery + where );
4083 while ( t.next() )
4084 {
4085 if ( t.value( 0 ).toString() != d->user )
4086 tl.append( t.value( 0 ).toString() + QLatin1Char( '.' ) + t.value( 1 ).toString() );
4087 else
4088 tl.append( t.value( 1 ).toString() );
4089 }
4090 }
4091 if ( type & QSql::Views )
4092 {
4093 const QLatin1String query( "select owner, view_name from all_views where " );
4094 const QString where = make_where_clause( user, AndExpression );
4095 t.exec( query + where );
4096 while ( t.next() )
4097 {
4098 if ( t.value( 0 ).toString().toUpper() != d->user.toUpper() )
4099 tl.append( t.value( 0 ).toString() + QLatin1Char( '.' ) + t.value( 1 ).toString() );
4100 else
4101 tl.append( t.value( 1 ).toString() );
4102 }
4103 }
4104 if ( type & QSql::SystemTables )
4105 {
4106 t.exec( QLatin1String( "select table_name from dictionary" ) );
4107 while ( t.next() )
4108 {
4109 tl.append( t.value( 0 ).toString() );
4110 }
4111 const QLatin1String tableQuery( "select owner, table_name from all_tables where " );
4112 const QString where = make_where_clause( user, OrExpression );
4113 t.exec( tableQuery + where );
4114 while ( t.next() )
4115 {
4116 if ( t.value( 0 ).toString().toUpper() != user.toUpper() )
4117 tl.append( t.value( 0 ).toString() + QLatin1Char( '.' ) + t.value( 1 ).toString() );
4118 else
4119 tl.append( t.value( 1 ).toString() );
4120 }
4121
4122 // list all table synonyms as well
4123 const QLatin1String synonymQuery( "select owner, synonym_name from all_synonyms where " );
4124 t.exec( synonymQuery + where );
4125 while ( t.next() )
4126 {
4127 if ( t.value( 0 ).toString() != d->user )
4128 tl.append( t.value( 0 ).toString() + QLatin1Char( '.' ) + t.value( 1 ).toString() );
4129 else
4130 tl.append( t.value( 1 ).toString() );
4131 }
4132 }
4133 return tl;
4134 }
4135
qSplitTableAndOwner(const QString & tname,QString * tbl,QString * owner)4136 void qSplitTableAndOwner( const QString &tname, QString *tbl,
4137 QString *owner )
4138 {
4139 ENTER
4140 int i = tname.indexOf( QLatin1Char( '.' ) ); // prefixed with owner?
4141 if ( i != -1 )
4142 {
4143 *tbl = tname.right( tname.length() - i - 1 );
4144 *owner = tname.left( i );
4145 }
4146 else
4147 {
4148 *tbl = tname;
4149 }
4150 }
4151
record(const QString & tablename) const4152 QSqlRecord QOCISpatialDriver::record( const QString &tablename ) const
4153 {
4154 ENTER
4155 Q_D( const QOCISpatialDriver );
4156 QSqlRecord fil;
4157 if ( !isOpen() )
4158 return fil;
4159
4160 QSqlQuery t( createResult() );
4161 // using two separate queries for this is A LOT faster than using,
4162 // e.g., a sub-query on the sys.synonyms table
4163 QString stmt( QLatin1String( "select column_name, data_type, data_length, "
4164 "data_precision, data_scale, nullable, data_default%1"
4165 "from all_tab_columns a "
4166 "where a.table_name=%2" ) );
4167 if ( d->serverVersion >= 9 )
4168 stmt = stmt.arg( QLatin1String( ", char_length " ) );
4169 else
4170 stmt = stmt.arg( QLatin1String( " " ) );
4171 bool buildRecordInfo = false;
4172 QString table, owner, tmpStmt;
4173 qSplitTableAndOwner( tablename, &table, &owner );
4174
4175 if ( isIdentifierEscaped( table, QSqlDriver::TableName ) )
4176 table = stripDelimiters( table, QSqlDriver::TableName );
4177 else
4178 table = table.toUpper();
4179
4180 tmpStmt = stmt.arg( QLatin1Char( '\'' ) + table + QLatin1Char( '\'' ) );
4181 if ( owner.isEmpty() )
4182 {
4183 owner = d->user;
4184 }
4185
4186 if ( isIdentifierEscaped( owner, QSqlDriver::TableName ) )
4187 owner = stripDelimiters( owner, QSqlDriver::TableName );
4188 else
4189 owner = owner.toUpper();
4190
4191 tmpStmt += QLatin1String( " and a.owner='" ) + owner + QLatin1Char( '\'' );
4192 t.setForwardOnly( true );
4193 t.exec( tmpStmt );
4194 if ( !t.next() ) // try and see if the tablename is a synonym
4195 {
4196 stmt = stmt + QLatin1String( " join all_synonyms b "
4197 "on a.owner=b.table_owner and a.table_name=b.table_name "
4198 "where b.owner='" ) + owner +
4199 QLatin1String( "' and b.synonym_name='" ) + table +
4200 QLatin1Char( '\'' );
4201 t.setForwardOnly( true );
4202 t.exec( stmt );
4203 if ( t.next() )
4204 buildRecordInfo = true;
4205 }
4206 else
4207 {
4208 buildRecordInfo = true;
4209 }
4210 QStringList keywords = QStringList()
4211 << QLatin1String( "NUMBER" )
4212 << QLatin1String( "FLOAT" )
4213 << QLatin1String( "BINARY_FLOAT" )
4214 << QLatin1String( "BINARY_DOUBLE" );
4215 if ( buildRecordInfo )
4216 {
4217 do
4218 {
4219 QVariant::Type ty = qDecodeOCIType( t.value( 1 ).toString(), t.numericalPrecisionPolicy() );
4220 QSqlField f( t.value( 0 ).toString(), ty );
4221 f.setRequired( t.value( 5 ).toString() == QLatin1String( "N" ) );
4222 f.setPrecision( t.value( 4 ).toInt() );
4223 if ( d->serverVersion >= 9 && ( ty == QVariant::String ) && !t.isNull( 3 ) && !keywords.contains( t.value( 1 ).toString() ) )
4224 {
4225 // Oracle9: data_length == size in bytes, char_length == amount of characters
4226 f.setLength( t.value( 7 ).toInt() );
4227 }
4228 else
4229 {
4230 f.setLength( t.value( t.isNull( 3 ) ? 2 : 3 ).toInt() );
4231 }
4232 f.setDefaultValue( t.value( 6 ) );
4233 fil.append( f );
4234 }
4235 while ( t.next() );
4236 }
4237 return fil;
4238 }
4239
primaryIndex(const QString & tablename) const4240 QSqlIndex QOCISpatialDriver::primaryIndex( const QString &tablename ) const
4241 {
4242 Q_D( const QOCISpatialDriver );
4243 QSqlIndex idx( tablename );
4244 if ( !isOpen() )
4245 return idx;
4246 QSqlQuery t( createResult() );
4247 QString stmt( QLatin1String( "select b.column_name, b.index_name, a.table_name, a.owner "
4248 "from all_constraints a, all_ind_columns b "
4249 "where a.constraint_type='P' "
4250 "and b.index_name = a.constraint_name "
4251 "and b.index_owner = a.owner" ) );
4252
4253 bool buildIndex = false;
4254 QString table, owner, tmpStmt;
4255 qSplitTableAndOwner( tablename, &table, &owner );
4256
4257 if ( isIdentifierEscaped( table, QSqlDriver::TableName ) )
4258 table = stripDelimiters( table, QSqlDriver::TableName );
4259 else
4260 table = table.toUpper();
4261
4262 tmpStmt = stmt + QLatin1String( " and a.table_name='" ) + table + QLatin1Char( '\'' );
4263 if ( owner.isEmpty() )
4264 {
4265 owner = d->user;
4266 }
4267
4268 if ( isIdentifierEscaped( owner, QSqlDriver::TableName ) )
4269 owner = stripDelimiters( owner, QSqlDriver::TableName );
4270 else
4271 owner = owner.toUpper();
4272
4273 tmpStmt += QLatin1String( " and a.owner='" ) + owner + QLatin1Char( '\'' );
4274 t.setForwardOnly( true );
4275 t.exec( tmpStmt );
4276
4277 if ( !t.next() )
4278 {
4279 stmt += QLatin1String( " and a.table_name=(select tname from sys.synonyms "
4280 "where sname='" ) + table + QLatin1String( "' and creator=a.owner)" );
4281 t.setForwardOnly( true );
4282 t.exec( stmt );
4283 if ( t.next() )
4284 {
4285 owner = t.value( 3 ).toString();
4286 buildIndex = true;
4287 }
4288 }
4289 else
4290 {
4291 buildIndex = true;
4292 }
4293 if ( buildIndex )
4294 {
4295 QSqlQuery tt( createResult() );
4296 tt.setForwardOnly( true );
4297 idx.setName( t.value( 1 ).toString() );
4298 do
4299 {
4300 tt.exec( QLatin1String( "select data_type from all_tab_columns where table_name='" ) +
4301 t.value( 2 ).toString() + QLatin1String( "' and column_name='" ) +
4302 t.value( 0 ).toString() + QLatin1String( "' and owner='" ) +
4303 owner + QLatin1Char( '\'' ) );
4304 if ( !tt.next() )
4305 {
4306 return QSqlIndex();
4307 }
4308 QSqlField f( t.value( 0 ).toString(), qDecodeOCIType( tt.value( 0 ).toString(), t.numericalPrecisionPolicy() ) );
4309 idx.append( f );
4310 }
4311 while ( t.next() );
4312 return idx;
4313 }
4314 return QSqlIndex();
4315 }
4316
formatValue(const QSqlField & field,bool trimStrings) const4317 QString QOCISpatialDriver::formatValue( const QSqlField &field, bool trimStrings ) const
4318 {
4319 ENTER
4320 switch ( field.type() )
4321 {
4322 case QVariant::DateTime:
4323 {
4324 QDateTime datetime = field.value().toDateTime();
4325 QString datestring;
4326 if ( datetime.isValid() )
4327 {
4328 datestring = QLatin1String( "TO_DATE('" ) + QString::number( datetime.date().year() )
4329 + QLatin1Char( '-' )
4330 + QString::number( datetime.date().month() ) + QLatin1Char( '-' )
4331 + QString::number( datetime.date().day() ) + QLatin1Char( ' ' )
4332 + QString::number( datetime.time().hour() ) + QLatin1Char( ':' )
4333 + QString::number( datetime.time().minute() ) + QLatin1Char( ':' )
4334 + QString::number( datetime.time().second() )
4335 + QLatin1String( "','YYYY-MM-DD HH24:MI:SS')" );
4336 }
4337 else
4338 {
4339 datestring = QLatin1String( "NULL" );
4340 }
4341 return datestring;
4342 }
4343 case QVariant::Time:
4344 {
4345 QDateTime datetime = field.value().toDateTime();
4346 QString datestring;
4347 if ( datetime.isValid() )
4348 {
4349 datestring = QLatin1String( "TO_DATE('" )
4350 + QString::number( datetime.time().hour() ) + QLatin1Char( ':' )
4351 + QString::number( datetime.time().minute() ) + QLatin1Char( ':' )
4352 + QString::number( datetime.time().second() )
4353 + QLatin1String( "','HH24:MI:SS')" );
4354 }
4355 else
4356 {
4357 datestring = QLatin1String( "NULL" );
4358 }
4359 return datestring;
4360 }
4361 case QVariant::Date:
4362 {
4363 QDate date = field.value().toDate();
4364 QString datestring;
4365 if ( date.isValid() )
4366 {
4367 datestring = QLatin1String( "TO_DATE('" ) + QString::number( date.year() ) +
4368 QLatin1Char( '-' ) +
4369 QString::number( date.month() ) + QLatin1Char( '-' ) +
4370 QString::number( date.day() ) + QLatin1String( "','YYYY-MM-DD')" );
4371 }
4372 else
4373 {
4374 datestring = QLatin1String( "NULL" );
4375 }
4376 return datestring;
4377 }
4378 default:
4379 break;
4380 }
4381 return QSqlDriver::formatValue( field, trimStrings );
4382 }
4383
handle() const4384 QVariant QOCISpatialDriver::handle() const
4385 {
4386 ENTER
4387 Q_D( const QOCISpatialDriver );
4388 return QVariant::fromValue( d->env );
4389 }
4390
escapeIdentifier(const QString & identifier,IdentifierType type) const4391 QString QOCISpatialDriver::escapeIdentifier( const QString &identifier, IdentifierType type ) const
4392 {
4393 ENTER
4394 QString res = identifier;
4395 if ( !identifier.isEmpty() && !isIdentifierEscaped( identifier, type ) )
4396 {
4397 res.replace( QLatin1Char( '"' ), QLatin1String( "\"\"" ) );
4398 res.prepend( QLatin1Char( '"' ) ).append( QLatin1Char( '"' ) );
4399 res.replace( QLatin1Char( '.' ), QLatin1String( "\".\"" ) );
4400 }
4401 return res;
4402 }
4403
4404 QT_END_NAMESPACE
4405
4406 // vim: set sw=4 expandtab :
4407