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, &paramp, 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 **>( &param ),
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 **>( &param ),
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