diff --git a/.gitignore b/.gitignore
index 171610db..c0e8f9f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@
/lib32
/lib64
/proj/dll/temp
+/proj/test/Win32
+/proj/test/x64
# WinMerge backups
*.bak
@@ -181,3 +183,6 @@ testlog.manifest
/nuget/*.nupkg
*Browse.VC.db-wal
*Browse.VC.db-shm
+/.vs
+*.recipe
+*.txt
diff --git a/ChangeLog b/ChangeLog
index a5e26714..627fa1b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2021-11-30 Version 4.7.4 Vincent Rogier vince.rogier@ocilib.net
+
+ * Fixes (C API)
+
+ - Issue 291: OCI_DirPathGetErrorRow() always return 0 since v4.7.0
+ - Issue 284: OCI_MsgGetID() returns a NULL ID after being queued with OCI_DequeuePut()
+ - Issue 288: OCI_GetString returns null on implicit conversion of a RAW value when it's longer than 64 bytes
+ - Issue 283: OCI_DequeueGet() returns null pointer when message has empty payload
+ - Issue 282: PL/SQL Server output wrong internal buffer size on some oracle versions
+ - Issue 281: ORA-00931 in OCI_TypeInfoGet() when using SYS.RAW type
+
+ * Fixes (C++ API)
+
+ - Issue 272: AddressSanitizer reports mismatched new[] vs delete
+ - Issue 278: ocilib::Object causes Segmentation fault
+
+ * Miscellaneous
+
+ - Updated compilers for MS Windows prebuilt binaries
+ * VS2022 17.0.1 for 32/64 bit DLLs
+ * VC runtime (statically linked) updated from vc142 to vc143
+
2021-03-09 Version 4.7.3 Vincent Rogier vince.rogier@ocilib.net
* Fixes (C API)
diff --git a/VERSION b/VERSION
index 87b18a56..b48b2de9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-4.7.3
+4.7.4
diff --git a/doxygen/Doxyfile.dox b/doxygen/Doxyfile.dox
index b30bf378..de10faae 100644
--- a/doxygen/Doxyfile.dox
+++ b/doxygen/Doxyfile.dox
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.15
+# Doxyfile 1.9.2
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -38,7 +38,7 @@ PROJECT_NAME = "OCILIB (C and C++ Driver for Oracle)"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 4.7.3
+PROJECT_NUMBER = 4.7.4
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -93,14 +93,6 @@ ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
-# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all generated output in the proper direction.
-# Possible values are: None, LTR, RTL and Context.
-# The default value is: None.
-
-OUTPUT_TEXT_DIRECTION = None
-
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@@ -126,9 +118,9 @@ REPEAT_BRIEF = YES
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
-ABBREVIATE_BRIEF = "The $name class " \
- "The $name widget " \
- "The $name file " \
+ABBREVIATE_BRIEF = "The $name class " \
+ "The $name widget " \
+ "The $name file " \
is \
provides \
specifies \
@@ -170,7 +162,7 @@ FULL_PATH_NAMES = NO
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH = "C:/Program Files/doxygen/bin/ "
+STRIP_FROM_PATH = "C:/Program Files/doxygen/bin/ "
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -197,6 +189,16 @@ SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -217,6 +219,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -240,25 +250,19 @@ TAB_SIZE = 4
# the documentation. An alias has the form:
# name=value
# For example adding
-# "sideeffect=@par Side Effects:\n"
+# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
-# "Side Effects:". You can put \n's in the value part of an alias to insert
-# newlines (in the resulting output). You can put ^^ in the value part of an
-# alias to insert a newline as if a physical newline was in the original file.
-# When you need a literal { or } or , in the value part of an alias you have to
-# escape them by means of a backslash (\), this can lead to conflicts with the
-# commands \{ and \} for these it is advised to use the version @{ and @} or use
-# a double escape (\\{ and \\})
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -299,19 +303,22 @@ OPTIMIZE_OUTPUT_SLICE = NO
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
-# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is
-# Fortran), use: inc=Fortran f=C.
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING = hpp=C++
@@ -329,7 +336,7 @@ MARKDOWN_SUPPORT = YES
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
-# Minimum value: 0, maximum value: 99, default value: 0.
+# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
@@ -445,6 +452,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -465,6 +485,12 @@ EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@@ -502,6 +528,13 @@ EXTRACT_LOCAL_METHODS = YES
EXTRACT_ANON_NSPACES = YES
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -519,8 +552,8 @@ HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@@ -539,11 +572,18 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
@@ -562,6 +602,12 @@ HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+SHOW_HEADERFILE = YES
+
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
@@ -719,7 +765,8 @@ FILE_VERSION_FILTER =
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
-# will be used as the name of the layout file.
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
@@ -765,24 +812,35 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some parameters
-# in a documented function, or documenting parameters that don't exist or using
-# markup commands wrongly.
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = YES
+
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
-# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation. If
-# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -827,8 +885,8 @@ INPUT = Introduction.txt \
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
@@ -841,11 +899,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
-# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
+# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
+# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cpp \
@@ -1067,16 +1129,24 @@ USE_HTAGS = NO
VERBATIM_HEADERS = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
-# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
-# cost of reduced performance. This can be particularly helpful with template
-# rich C++ code for which doxygen's built-in parser lacks the necessary type
-# information.
+# clang parser (see:
+# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
+# performance. This can be particularly helpful with template rich C++ code for
+# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
+# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
+# tag is set to YES then doxygen will add the directory of each input to the
+# include path.
+# The default value is: YES.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_ADD_INC_PATHS = YES
+
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
@@ -1086,10 +1156,13 @@ CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
-# path to the compilation database (see:
-# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
-# were built. This is equivalent to specifying the "-p" option to a clang tool,
-# such as clang-check. These options will then be passed to the parser.
+# path to the directory containing a file called compile_commands.json. This
+# file is the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
+# options used when the source files were built. This is equivalent to
+# specifying the -p option to a clang tool, such as clang-check. These options
+# will then be passed to the parser. Any options specified with CLANG_OPTIONS
+# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
@@ -1106,13 +1179,6 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = NO
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1212,7 +1278,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
-# this color. Hue is specified as an angle on a colorwheel, see
+# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
@@ -1222,7 +1288,7 @@ HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 222
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
-# in the HTML output. For a value of 0 the output will use grayscales only. A
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1251,9 +1317,9 @@ HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
+# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
+# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1283,10 +1349,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1328,8 +1395,12 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1359,7 +1430,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1404,7 +1475,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1412,8 +1484,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1421,30 +1493,30 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1487,16 +1559,28 @@ DISABLE_INDEX = NO
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
-# further fine-tune the look of the index. As an example, the default style
-# sheet generated by doxygen has an example that shows how to put an image at
-# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
-# the same information as the tab index, you could consider setting
-# DISABLE_INDEX to YES when enabling this option.
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATOR_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FULL_SIDEBAR = NO
+
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
@@ -1521,6 +1605,17 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1541,8 +1636,14 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1552,11 +1653,29 @@ FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION = MathJax_2
+
# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
-# compatibility), NativeMML (i.e. MathML) and SVG.
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1569,22 +1688,29 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
-# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1612,7 +1738,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1631,7 +1757,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/).
+# Xapian (see:
+# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1644,8 +1771,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/). See the section "External Indexing and
-# Searching" for details.
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -1716,10 +1844,11 @@ LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
-# generate index for LaTeX.
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
-# The default value is: \makeindex.
+# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = \makeindex
@@ -1739,7 +1868,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
@@ -1753,29 +1882,31 @@ PAPER_TYPE = a4wide
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
-# generated LaTeX document. The header should contain everything until the first
-# chapter. If it is left blank doxygen will generate a standard header. See
-# section "Doxygen usage" for information on how to let doxygen write the
-# default header to a separate file.
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
#
-# Note: Only use a user-defined header if you know what you are doing! The
-# following commands have a special meaning inside the header: $title,
-# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
-# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
-# string, for the replacement values of the other commands the user is referred
-# to HTML_HEADER.
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
-# generated LaTeX document. The footer should contain everything after the last
-# chapter. If it is left blank doxygen will generate a standard footer. See
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
-# special commands can be used inside the footer.
-#
-# Note: Only use a user-defined footer if you know what you are doing!
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
@@ -1808,9 +1939,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = NO
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1818,8 +1951,7 @@ USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
-# if errors occur, instead of asking the user for help. This option is also used
-# when generating formulas in HTML.
+# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -1832,16 +1964,6 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE = NO
-
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@@ -1922,16 +2044,6 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
-# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
-# with syntax highlighting in the RTF output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_RTF is set to YES.
-
-RTF_SOURCE_CODE = NO
-
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@@ -2028,15 +2140,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
-# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
-# program listings (including syntax highlighting and cross-referencing
-# information) to the DOCBOOK output. Note that enabling this will significantly
-# increase the size of the DOCBOOK output.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
-
-DOCBOOK_PROGRAMLISTING = NO
-
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@@ -2049,6 +2152,10 @@ DOCBOOK_PROGRAMLISTING = NO
GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to Sqlite3 output
+#---------------------------------------------------------------------------
+
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2211,12 +2318,6 @@ EXTERNAL_GROUPS = NO
EXTERNAL_PAGES = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@@ -2230,15 +2331,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = NO
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@@ -2336,10 +2428,32 @@ UML_LOOK = NO
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
+# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2529,9 +2643,11 @@ DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
diff --git a/doxygen/Introduction.txt b/doxygen/Introduction.txt
index cb500f81..31c2f0ab 100644
--- a/doxygen/Introduction.txt
+++ b/doxygen/Introduction.txt
@@ -17,7 +17,7 @@
@section Version Version information
-Current version : 4.7.3
+Current version : 4.7.4
@section Features Main features
diff --git a/include/ocilibc/api.h b/include/ocilibc/api.h
index d6785504..9819803d 100644
--- a/include/ocilibc/api.h
+++ b/include/ocilibc/api.h
@@ -13067,12 +13067,13 @@ OCI_SYM_PUBLIC OCI_Connection * OCI_API OCI_TypeInfoGetConnection
* @param typinf - Type info handle
*
* @note
- * this call is optional.
+ * this call is DEPRECATED.
* OCI_TypeInfo object are internally tracked and
* automatically freed when their related connection is freed
+ * Thus this method does nothing
*
* @return
- * TRUE on success otherwise FALSE
+ * TRUE if input handle is valid otherwise FALSE
*
*/
@@ -16311,7 +16312,7 @@ OCI_SYM_PUBLIC boolean OCI_API OCI_QueueStop
* @note
* Possible values for parameter 'queue_payload_type' :
* - For Oracle types (UDT) : use the type name ([schema.].type_name)
- * - For RAW data : use "SYS.RAW" or "RAW"
+ * - For RAW data : use "SYS.RAW" or "RAW" (depending on Oracle versions - For latest ones, use "RAW")
*
* @note
* Possible values for parameter 'message_grouping' :
diff --git a/include/ocilibcpp/detail/support/BindObjectAdaptor.hpp b/include/ocilibcpp/detail/support/BindObjectAdaptor.hpp
index 5981f3fc..fb0dee2c 100644
--- a/include/ocilibcpp/detail/support/BindObjectAdaptor.hpp
+++ b/include/ocilibcpp/detail/support/BindObjectAdaptor.hpp
@@ -72,7 +72,7 @@ namespace ocilib
template
BindObjectAdaptor::~BindObjectAdaptor() noexcept
{
- delete core::OnDeallocate(_data);
+ delete[] core::OnDeallocate(_data);
}
template
diff --git a/nuget/ocilib.nuspec b/nuget/ocilib.nuspec
index c9173587..4290a770 100644
--- a/nuget/ocilib.nuspec
+++ b/nuget/ocilib.nuspec
@@ -2,7 +2,7 @@
ocilib
- 4.7.3
+ 4.7.4
Ocilib Driver for Oracle databases
OCILIB is an open source and cross platform Oracle Driver delivering efficient access to Oracle databases.
@@ -18,7 +18,7 @@ It requires Oracle Client libraries (Regular or instant client).
Vincent Rogier
Vincentt Rogier
- https://github.com/vrogier/ocilib/releases/tag/v4.7.3
+ https://github.com/vrogier/ocilib/releases/tag/v4.7.4
https://github.com/vrogier/ocilib
http://ocilib.net/images/logo-160x120.png
images\logo-160x120.png
@@ -27,7 +27,7 @@ It requires Oracle Client libraries (Regular or instant client).
https://licenses.nuget.org/Apache-2.0
Copyright 2007-2021 Vincent Rogier
Oracle SQL C C++ OCI API Access Driver
-
+
diff --git a/ocilib.pc b/ocilib.pc
index b5993bfb..f6eb90a9 100644
--- a/ocilib.pc
+++ b/ocilib.pc
@@ -5,7 +5,7 @@ includedir=${prefix}/include
Name: ocilib
Description: OCILIB is an open source and cross platform Oracle Driver that delivers efficient access to Oracle databases.
URL: http://www.ocilib.net
-Version: 4.7.3
+Version: 4.7.4
Libs: -L${exec_prefix}/lib -locilib
Cflags: -I${prefix}/include
diff --git a/proj/dll/main.rc b/proj/dll/main.rc
index 35c546ef..d3499b4e 100644
--- a/proj/dll/main.rc
+++ b/proj/dll/main.rc
@@ -2,8 +2,8 @@
#include "resource.h"
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,7,3,0
- PRODUCTVERSION 4,7,3,0
+ FILEVERSION 4,7,4,0
+ PRODUCTVERSION 4,7,4,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -20,11 +20,11 @@ BEGIN
BEGIN
VALUE "CompanyName", "Vincent Rogier"
VALUE "FileDescription", "Open source C driver for Oracle databases"
- VALUE "FileVersion", "4.7.3.0"
+ VALUE "FileVersion", "4.7.4.0"
VALUE "InternalName", "OCILIB"
VALUE "LegalCopyright", "Copyright © 2007-2021"
VALUE "ProductName", "OCILIB (C driver for Oracle)"
- VALUE "ProductVersion", "4.7.3.0"
+ VALUE "ProductVersion", "4.7.4.0"
END
END
BLOCK "VarFileInfo"
@@ -34,8 +34,8 @@ BEGIN
END
IDR_VERSION VERSIONINFO
- FILEVERSION 4,7,3,0
- PRODUCTVERSION 4,7,3,0
+ FILEVERSION 4,7,4,0
+ PRODUCTVERSION 4,7,4,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,12 +52,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Vincent Rogier"
VALUE "FileDescription", "Open source C driver for Oracle databases"
- VALUE "FileVersion", "4.7.3.0"
+ VALUE "FileVersion", "4.7.4.0"
VALUE "InternalName", "OCILIB.dll"
VALUE "LegalCopyright", "Copyright © 2007-2021"
VALUE "OriginalFilename", "OCILIB.dll"
VALUE "ProductName", "OCILIB (C driver for Oracle)"
- VALUE "ProductVersion", "4.7.3.0"
+ VALUE "ProductVersion", "4.7.4.0"
END
END
BLOCK "VarFileInfo"
diff --git a/proj/dll/ocilib_dll_vs2019.sln b/proj/dll/ocilib_dll.sln
similarity index 94%
rename from proj/dll/ocilib_dll_vs2019.sln
rename to proj/dll/ocilib_dll.sln
index b86a2775..d452c592 100644
--- a/proj/dll/ocilib_dll_vs2019.sln
+++ b/proj/dll/ocilib_dll.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29926.136
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OCILIB Dynamic Library", "ocilib_dll_vs2019.vcxproj", "{B5B9DE0E-C2BE-4D75-BDA0-CBF81BD18D7D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OCILIB Dynamic Library", "ocilib_dll.vcxproj", "{B5B9DE0E-C2BE-4D75-BDA0-CBF81BD18D7D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/proj/dll/ocilib_dll_vs2019.vcxproj b/proj/dll/ocilib_dll.vcxproj
similarity index 99%
rename from proj/dll/ocilib_dll_vs2019.vcxproj
rename to proj/dll/ocilib_dll.vcxproj
index 2d5b3bfa..1caf1d36 100644
--- a/proj/dll/ocilib_dll_vs2019.vcxproj
+++ b/proj/dll/ocilib_dll.vcxproj
@@ -30,25 +30,25 @@
DynamicLibrary
Unicode
true
- v142
+ v143
DynamicLibrary
MultiByte
true
- v142
+ v143
DynamicLibrary
Unicode
true
- v142
+ v143
DynamicLibrary
MultiByte
true
- v142
+ v143
diff --git a/proj/dll/ocilib_dll_vs2019.vcxproj.filters b/proj/dll/ocilib_dll.vcxproj.filters
similarity index 100%
rename from proj/dll/ocilib_dll_vs2019.vcxproj.filters
rename to proj/dll/ocilib_dll.vcxproj.filters
diff --git a/proj/test/ocilib++_demo_vs2019.sln b/proj/test/ocilib++_demo.sln
similarity index 94%
rename from proj/test/ocilib++_demo_vs2019.sln
rename to proj/test/ocilib++_demo.sln
index 1e4a90d8..393adc24 100644
--- a/proj/test/ocilib++_demo_vs2019.sln
+++ b/proj/test/ocilib++_demo.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29926.136
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ocilib++_demo_vs2019", "ocilib++_demo_vs2019.vcxproj", "{22508194-4A82-4EA7-A754-126534AE7108}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ocilib++_demo", "ocilib++_demo.vcxproj", "{22508194-4A82-4EA7-A754-126534AE7108}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/proj/test/ocilib++_demo_vs2019.vcxproj b/proj/test/ocilib++_demo.vcxproj
similarity index 97%
rename from proj/test/ocilib++_demo_vs2019.vcxproj
rename to proj/test/ocilib++_demo.vcxproj
index 21e4c5b3..bd89c038 100644
--- a/proj/test/ocilib++_demo_vs2019.vcxproj
+++ b/proj/test/ocilib++_demo.vcxproj
@@ -32,27 +32,27 @@
Application
true
MultiByte
- v142
+ v143
Application
true
MultiByte
- v142
+ v143
Application
false
true
Unicode
- v142
+ v143
Application
false
true
Unicode
- v142
+ v143
@@ -113,7 +113,7 @@
Level3
Disabled
- WIN32;_DEBUG;OCI_CHARSET_WIDE;_CONSOLE;%(PreprocessorDefinitions)
+ WIN32;_DEBUG;OCI_CHARSET_ANSI;_CONSOLE;%(PreprocessorDefinitions)
diff --git a/proj/test/ocilib_demo_vs2019.sln b/proj/test/ocilib_demo.sln
similarity index 95%
rename from proj/test/ocilib_demo_vs2019.sln
rename to proj/test/ocilib_demo.sln
index 7e707543..d39aa247 100644
--- a/proj/test/ocilib_demo_vs2019.sln
+++ b/proj/test/ocilib_demo.sln
@@ -1,6 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 11.00
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OCILIB Demo", "ocilib_demo_vs2019.vcxproj", "{D08196A4-17BC-42CE-A7A9-866905120974}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OCILIB Demo", "ocilib_demo.vcxproj", "{D08196A4-17BC-42CE-A7A9-866905120974}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/proj/test/ocilib_demo_vs2019.vcxproj b/proj/test/ocilib_demo.vcxproj
similarity index 98%
rename from proj/test/ocilib_demo_vs2019.vcxproj
rename to proj/test/ocilib_demo.vcxproj
index 0dcfda65..14625f93 100644
--- a/proj/test/ocilib_demo_vs2019.vcxproj
+++ b/proj/test/ocilib_demo.vcxproj
@@ -190,6 +190,7 @@
MachineX86
kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ false
true
@@ -238,6 +239,7 @@
MachineX64
+ false
true
@@ -287,6 +289,7 @@
MachineX86
+ false
true
@@ -336,6 +339,7 @@
MachineX64
+ false
true
@@ -384,6 +388,7 @@
MachineX86
+ false
true
@@ -432,6 +437,7 @@
MachineX64
+ false
true
diff --git a/src/array.c b/src/array.c
index f4905603..a2d6e602 100644
--- a/src/array.c
+++ b/src/array.c
@@ -223,7 +223,14 @@ OCI_Array * OcilibArrayCreate
/* create array object */
- arr = OcilibListAppend(Env.arrs, sizeof(*arr));
+ LOCK_LIST
+ (
+ Env.arrs,
+ {
+ arr = OcilibListAppend(Env.arrs, sizeof(*arr));
+ }
+ )
+
CHECK_NULL(arr)
arr->con = con;
@@ -297,10 +304,20 @@ boolean OcilibArrayFreeFromHandles
CHECK_PTR(OCI_IPC_VOID, handles)
- arr = OcilibListFind(Env.arrs, (POCI_LIST_FIND) OcilibArrayFindAny, handles);
+ LOCK_LIST
+ (
+ Env.arrs,
+ {
+ arr = OcilibListFind(Env.arrs, (POCI_LIST_FIND)OcilibArrayFindAny, handles);
+ if (NULL != arr)
+ {
+ OcilibListRemove(Env.arrs, arr);
+ }
+ }
+ )
+
CHECK_NULL(arr)
- OcilibListRemove(Env.arrs, arr);
OcilibArrayDispose(arr);
FREE(arr)
diff --git a/src/callback.c b/src/callback.c
index 88f1d3ab..b185de85 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -632,7 +632,13 @@ void OcilibCallbackHAEvent
/* notify all related connections */
- OcilibListForEachWithParam(Env.cons, ¶ms, (POCI_LIST_FOR_EACH_WITH_PARAM)OcilibProcHAEventInvoke);
+ LOCK_LIST
+ (
+ Env.cons,
+ {
+ OcilibListForEachWithParam(Env.cons, ¶ms, (POCI_LIST_FOR_EACH_WITH_PARAM)OcilibProcHAEventInvoke);
+ }
+ )
/* get next server */
diff --git a/src/connection.c b/src/connection.c
index 73aaa429..c232144b 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -112,7 +112,16 @@ OCI_Connection * OcilibConnectionAllocate
/* create connection object */
- OCI_Connection *con = OcilibListAppend(Env.cons, sizeof(*con));
+ OCI_Connection* con = NULL;
+
+ LOCK_LIST
+ (
+ Env.cons,
+ {
+ con = OcilibListAppend(Env.cons, sizeof(*con));
+ }
+ )
+
CHECK_NULL(con)
con->alloc_handles = (0 == (mode & OCI_SESSION_XA));
@@ -958,25 +967,48 @@ static boolean OcilibConnectionLogOff
}
/* dissociate connection from existing subscriptions */
-
- OcilibListForEachWithParam(Env.subs, con, (POCI_LIST_FOR_EACH_WITH_PARAM) OcilibConnectionDetachSubscriptions);
+ LOCK_LIST
+ (
+ Env.subs,
+ {
+ OcilibListForEachWithParam(Env.subs, con, (POCI_LIST_FOR_EACH_WITH_PARAM)OcilibConnectionDetachSubscriptions);
+ }
+ )
WARNING_DISABLE_CAST_FUNC_TYPE
/* free all statements */
- OcilibListForEach(con->stmts, (POCI_LIST_FOR_EACH)OcilibStatementDispose);
- OcilibListClear(con->stmts);
+ LOCK_LIST
+ (
+ con->stmts,
+ {
+ OcilibListForEach(con->stmts, (POCI_LIST_FOR_EACH)OcilibStatementDispose);
+ OcilibListClear(con->stmts);
+ }
+ )
/* free all type info objects */
- OcilibListForEach(con->tinfs, (POCI_LIST_FOR_EACH)OcilibTypeInfoDispose);
- OcilibListClear(con->tinfs);
+ LOCK_LIST
+ (
+ con->tinfs,
+ {
+ OcilibListForEach(con->tinfs, (POCI_LIST_FOR_EACH)OcilibTypeInfoDispose);
+ OcilibListClear(con->tinfs);
+ }
+ )
/* free all transactions */
- OcilibListForEach(con->trsns, (POCI_LIST_FOR_EACH)OcilibTransactionDispose);
- OcilibListClear(con->trsns);
+ LOCK_LIST
+ (
+ con->trsns,
+ {
+ OcilibListForEach(con->trsns, (POCI_LIST_FOR_EACH)OcilibTransactionDispose);
+ OcilibListClear(con->trsns);
+ }
+ )
WARNING_RESTORE_CAST_FUNC_TYPE
@@ -1211,8 +1243,15 @@ boolean OcilibConnectionFree
CHECK_PTR(OCI_IPC_CONNECTION, con)
- OcilibConnectionDispose(con);
- OcilibListRemove(Env.cons, con);
+ LOCK_LIST
+ (
+ Env.cons,
+ {
+ OcilibListRemove(Env.cons, con);
+ }
+ )
+
+ CHECK(OcilibConnectionDispose(con))
FREE(con)
@@ -1916,15 +1955,13 @@ boolean OcilibConnectionEnableServerOutput
{
const unsigned int charsize = sizeof(otext);
- /* check parameter ranges ( Oracle 10g increased the size of output line */
+ const unsigned int max_lnsize = (con->ver_num >= OCI_10_2) ? OCI_OUPUT_LSIZE_10G : OCI_OUPUT_LSIZE;
- if (con->ver_num >= OCI_10_2 && lnsize > OCI_OUPUT_LSIZE_10G)
- {
- lnsize = OCI_OUPUT_LSIZE_10G;
- }
- else if (lnsize > OCI_OUPUT_LSIZE)
+ /* check parameter ranges (Oracle 10g increased the size of output line) */
+
+ if (lnsize > max_lnsize)
{
- lnsize = OCI_OUPUT_LSIZE;
+ lnsize = max_lnsize;
}
con->svopt->arrsize = arrsize;
diff --git a/src/dequeue.c b/src/dequeue.c
index 957a3155..f83eb96c 100644
--- a/src/dequeue.c
+++ b/src/dequeue.c
@@ -328,26 +328,27 @@ OCI_Msg * OcilibDequeueGetMessage
if (OCI_SUCCESSFUL(ret))
{
- /* get payload */
+ msg = dequeue->msg;
- if (OCI_UNKNOWN != dequeue->typinf->typecode)
+ /* get payload */
+
+ if (p_ind && (OCI_IND_NULL != (*(OCIInd *) p_ind)))
{
- if (p_ind && (OCI_IND_NULL != (*(OCIInd *) p_ind)))
+ dequeue->msg->ind = *(OCIInd *) p_ind;
+
+ if (OCI_UNKNOWN != dequeue->typinf->typecode)
{
- dequeue->msg->ind = *(OCIInd *) p_ind;
dequeue->msg->obj = OcilibObjectInitialize
- (
- dequeue->typinf->con,
- (OCI_Object*)dequeue->msg->obj,
- dequeue->msg->payload,
- dequeue->typinf,
- NULL, -1, TRUE
- );
+ (
+ dequeue->typinf->con,
+ (OCI_Object*)dequeue->msg->obj,
+ dequeue->msg->payload,
+ dequeue->typinf,
+ NULL, -1, TRUE
+ );
CHECK_NULL(dequeue->msg->obj)
-
- msg = dequeue->msg;
}
}
}
diff --git a/src/dirpath.c b/src/dirpath.c
index 81b856f0..e234286c 100644
--- a/src/dirpath.c
+++ b/src/dirpath.c
@@ -143,7 +143,7 @@ static unsigned int OcilibDirPathArrayToStream
if (OCI_DCM_DEFAULT == dp->cvt_mode)
{
- THROW(OcilibExceptionOCI, dp->con->err, ret)
+ OcilibExceptionOCI(&call_context, dp->con->err, ret);
}
break;
}
@@ -246,7 +246,7 @@ static unsigned int OcilibDirPathLoadStream
case OCI_ERROR:
{
status = OCI_DPR_ERROR;
- THROW(OcilibExceptionOCI, dp->con->err, ret)
+ OcilibExceptionOCI(&call_context, dp->con->err, ret);
break;
}
case OCI_NO_DATA:
diff --git a/src/enqueue.c b/src/enqueue.c
index b65d6cd0..05f5ea21 100644
--- a/src/enqueue.c
+++ b/src/enqueue.c
@@ -145,13 +145,10 @@ boolean OcilibEnqueuePut
/* get payload */
- if (OCI_UNKNOWN != enqueue->typinf->typecode)
+ if (OCI_UNKNOWN != enqueue->typinf->typecode && OCI_IND_NULL != msg->ind)
{
- if (OCI_IND_NULL != msg->ind)
- {
- payload = msg->obj->handle;
- ind = msg->obj->tab_ind;
- }
+ payload = msg->obj->handle;
+ ind = msg->obj->tab_ind;
}
else
{
@@ -186,7 +183,7 @@ boolean OcilibEnqueuePut
ret = OCIAQEnq(enqueue->typinf->con->cxt, enqueue->typinf->con->err,
(OraText*)name, enqueue->opth, msg->proph,
- enqueue->typinf->tdo, &payload, &ind, NULL, OCI_DEFAULT);
+ enqueue->typinf->tdo, &payload, &ind, &msg->id, OCI_DEFAULT);
/* check returned error code */
diff --git a/src/environment.c b/src/environment.c
index b9920e6f..2b997948 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -419,6 +419,7 @@ OCIARRAYDESCRIPTORALLOC OCIArrayDescriptorAlloc = NULL;
OCIARRAYDESCRIPTORFREE OCIArrayDescriptorFree = NULL;
OCICLIENTVERSION OCIClientVersion = NULL;
OCITYPEBYREF OCITypeByRef = NULL;
+OCITYPEBYNAME OCITypeByName = NULL;
OCINUMBERTOINT OCINumberToInt = NULL;
OCINUMBERFROMINT OCINumberFromInt = NULL;
OCINUMBERTOREAL OCINumberToReal = NULL;
@@ -1071,6 +1072,8 @@ boolean OcilibEnvironmentInitialize
LIB_SYMBOL(Env.lib_handle, "OCITypeByRef", OCITypeByRef,
OCITYPEBYREF);
+ LIB_SYMBOL(Env.lib_handle, "OCITypeByName", OCITypeByName,
+ OCITYPEBYNAME);
LIB_SYMBOL(Env.lib_handle, "OCINumberToInt", OCINumberToInt,
OCINUMBERTOINT);
@@ -1527,29 +1530,47 @@ boolean OcilibEnvironmentCleanup
/* dispose list items */
- OcilibListForEach(Env.arrs, (POCI_LIST_FOR_EACH)OcilibArrayDispose);
- OcilibListForEach(Env.subs, (POCI_LIST_FOR_EACH)OcilibSubscriptionDispose);
- OcilibListForEach(Env.cons, (POCI_LIST_FOR_EACH)OcilibConnectionDispose);
- OcilibListForEach(Env.pools, (POCI_LIST_FOR_EACH)OcilibPoolDispose);
-
- /* free all arrays */
+ LOCK_LIST
+ (
+ Env.arrs,
+ {
+ OcilibListForEach(Env.arrs, (POCI_LIST_FOR_EACH)OcilibArrayDispose);
+ OcilibListClear(Env.arrs);
+ }
+ )
- OcilibListClear(Env.arrs);
- OcilibListFree(Env.arrs);
+ LOCK_LIST
+ (
+ Env.subs,
+ {
+ OcilibListForEach(Env.subs, (POCI_LIST_FOR_EACH)OcilibSubscriptionDispose);
+ OcilibListClear(Env.subs);
+ }
+ )
- /* free all subscriptions */
+ LOCK_LIST
+ (
+ Env.cons,
+ {
+ OcilibListForEach(Env.cons, (POCI_LIST_FOR_EACH)OcilibConnectionDispose);
+ OcilibListClear(Env.cons);
+ }
+ )
- OcilibListClear(Env.subs);
- OcilibListFree(Env.subs);
+ LOCK_LIST
+ (
+ Env.pools,
+ {
+ OcilibListForEach(Env.pools, (POCI_LIST_FOR_EACH)OcilibPoolDispose);
+ OcilibListClear(Env.pools);
+ }
+ )
- /* free all connections */
+ /* free all lists */
- OcilibListClear(Env.cons);
+ OcilibListFree(Env.arrs);
+ OcilibListFree(Env.subs);
OcilibListFree(Env.cons);
-
- /* free all pools */
-
- OcilibListClear(Env.pools);
OcilibListFree(Env.pools);
/* free key map */
diff --git a/src/import.h b/src/import.h
index 1a4e928c..b498d5c0 100644
--- a/src/import.h
+++ b/src/import.h
@@ -186,6 +186,7 @@ extern OCIARRAYDESCRIPTORALLOC OCIArrayDescriptorAlloc;
extern OCIARRAYDESCRIPTORFREE OCIArrayDescriptorFree;
extern OCICLIENTVERSION OCIClientVersion;
extern OCITYPEBYREF OCITypeByRef;
+extern OCITYPEBYNAME OCITypeByName;
extern OCINUMBERTOINT OCINumberToInt;
extern OCINUMBERFROMINT OCINumberFromInt;
extern OCINUMBERTOREAL OCINumberToReal;
diff --git a/src/list.c b/src/list.c
index b6c41fcc..7ac0de1c 100644
--- a/src/list.c
+++ b/src/list.c
@@ -24,35 +24,18 @@
#include "memory.h"
#include "mutex.h"
-#define ACQUIRE_LOCK() \
- \
- if (NULL != list->mutex) \
- { \
- CHECK(OcilibMutexAcquire(list->mutex)) \
- }
-
-#define RELEASE_LOCK() \
- \
- if (NULL != list->mutex) \
- { \
- CHECK(OcilibMutexRelease(list->mutex)) \
- }
-
#define LIST_FOR_EACH(exp) \
\
if (list) \
{ \
OCI_Item *item = NULL; \
- ACQUIRE_LOCK() \
item = list->head; \
while (item) \
{ \
exp; \
item = item->next; \
} \
- RELEASE_LOCK() \
}
-
/* --------------------------------------------------------------------------------------------- *
* OcilibListCreateItem
* --------------------------------------------------------------------------------------------- */
@@ -171,6 +154,7 @@ boolean OcilibListFree
if (NULL!= list->mutex)
{
OcilibMutexFree(list->mutex);
+ list->mutex = NULL;
}
OcilibErrorResetSource(NULL, list);
@@ -203,8 +187,6 @@ void * OcilibListAppend
CHECK_PTR(OCI_IPC_LIST, list)
- ACQUIRE_LOCK()
-
item = OcilibListCreateItem(list->type, size);
CHECK_NULL(item)
@@ -226,8 +208,6 @@ void * OcilibListAppend
list->count++;
- RELEASE_LOCK()
-
SET_RETVAL(item->data)
EXIT_FUNC()
@@ -252,8 +232,6 @@ boolean OcilibListClear
CHECK_PTR(OCI_IPC_LIST, list)
- ACQUIRE_LOCK()
-
/* walk along the list to free item's buffer */
item = list->head;
@@ -273,8 +251,6 @@ boolean OcilibListClear
list->head = NULL;
list->count = 0;
- RELEASE_LOCK()
-
SET_SUCCESS()
EXIT_FUNC()
@@ -355,8 +331,6 @@ boolean OcilibListRemove
CHECK_PTR(OCI_IPC_LIST, list)
CHECK_PTR(OCI_IPC_VOID, data)
- ACQUIRE_LOCK()
-
item = list->head;
boolean found = FALSE;
@@ -394,8 +368,6 @@ boolean OcilibListRemove
list->count--;
}
- RELEASE_LOCK()
-
SET_RETVAL(found)
EXIT_FUNC()
@@ -467,3 +439,58 @@ void * OcilibListFind
EXIT_FUNC()
}
+
+/* --------------------------------------------------------------------------------------------- *
+* OcilibListLock
+* --------------------------------------------------------------------------------------------- */
+
+boolean OcilibListLock
+(
+ OCI_List* list
+)
+{
+ ENTER_FUNC
+ (
+ /* returns */ boolean, FALSE,
+ /* context */ OCI_IPC_LIST, list
+ )
+
+ CHECK_PTR(OCI_IPC_LIST, list)
+
+ if (NULL != list->mutex)
+ {
+ CHECK(OcilibMutexAcquire(list->mutex))
+ }
+
+ SET_SUCCESS()
+
+ EXIT_FUNC()
+}
+
+/* --------------------------------------------------------------------------------------------- *
+* OcilibListUnlock
+* --------------------------------------------------------------------------------------------- */
+
+boolean OcilibListUnlock
+(
+ OCI_List* list
+)
+{
+ ENTER_FUNC
+ (
+ /* returns */ boolean, FALSE,
+ /* context */ OCI_IPC_LIST, list
+ )
+
+ CHECK_PTR(OCI_IPC_LIST, list)
+
+ if (NULL != list->mutex)
+ {
+ CHECK(OcilibMutexRelease(list->mutex))
+ }
+
+ SET_SUCCESS()
+
+ EXIT_FUNC()
+}
+
diff --git a/src/list.h b/src/list.h
index b75fed92..26057a49 100644
--- a/src/list.h
+++ b/src/list.h
@@ -93,4 +93,14 @@ OCI_SYM_LOCAL void * OcilibListFind
void * param
);
+OCI_SYM_LOCAL boolean OcilibListLock
+(
+ OCI_List* list
+);
+
+OCI_SYM_LOCAL boolean OcilibListUnlock
+(
+ OCI_List* list
+);
+
#endif /* OCILIB_LIST_H_INCLUDED */
diff --git a/src/macros.h b/src/macros.h
index 25783b48..aab86ec5 100644
--- a/src/macros.h
+++ b/src/macros.h
@@ -472,6 +472,13 @@ ExitLabel: \
( \
err, OCIAttrSet, (void*) (handle), (ub4) (htype), (void*) (value), \
(ub4) (size), (ub4) (atype), err \
- ) \
+ )
+
+
+#define LOCK_LIST(list, exp) \
+ \
+ OcilibListLock(list); \
+ exp \
+ OcilibListUnlock(list); \
#endif /* OCILIB_MACROS_H_INCLUDED */
diff --git a/src/message.c b/src/message.c
index 0d14a559..0af44ee0 100644
--- a/src/message.c
+++ b/src/message.c
@@ -122,7 +122,7 @@ boolean OcilibMessageFree
/* free message RAW payload if necessary */
- if (NULL != msg->id && (OCI_UNKNOWN == msg->typinf->typecode))
+ if (NULL != msg->payload && (OCI_UNKNOWN == msg->typinf->typecode))
{
CHECK_OCI
(
@@ -133,6 +133,8 @@ boolean OcilibMessageFree
)
}
+ msg->payload = NULL;
+
/* free message ID */
if (NULL != msg->id)
@@ -653,21 +655,17 @@ boolean OcilibMessageGetID
CHECK_PTR(OCI_IPC_VOID, id)
CHECK_PTR(OCI_IPC_VOID, len)
+ unsigned int max_len = *len;
+ *len = 0;
+
if (msg->id)
{
const ub4 raw_len = OCIRawSize(msg->typinf->con->env, msg->id);
- if (*len > raw_len)
- {
- *len = raw_len;
- }
+ *len = min(max_len, raw_len);
memcpy(id, OCIRawPtr(msg->typinf->con->env, msg->id), (size_t) (*len));
}
- else
- {
- *len = 0;
- }
SET_SUCCESS()
@@ -692,11 +690,13 @@ boolean OcilibMessageGetOriginalID
)
OCIRaw *value = NULL;
+ unsigned int max_len = 0;
CHECK_PTR(OCI_IPC_MSG, msg)
CHECK_PTR(OCI_IPC_VOID, id)
CHECK_PTR(OCI_IPC_VOID, len)
+ max_len = *len;
*len = 0;
CHECK_ATTRIB_GET
@@ -710,10 +710,7 @@ boolean OcilibMessageGetOriginalID
{
const ub4 raw_len = OCIRawSize(msg->typinf->con->env, value);
- if (*len > raw_len)
- {
- *len = raw_len;
- }
+ *len = min(max_len, raw_len);
memcpy(id, OCIRawPtr(msg->typinf->con->env, value), (size_t) (*len));
}
@@ -755,7 +752,7 @@ boolean OcilibMessageSetOriginalID
CHECK_ATTRIB_SET
(
OCI_DTYPE_AQMSG_PROPERTIES, OCI_ATTR_ORIGINAL_MSGID,
- msg->proph, &value, 0,
+ msg->proph, value, 0,
msg->typinf->con->err
)
diff --git a/src/object.c b/src/object.c
index 9ca98d66..c66312ab 100644
--- a/src/object.c
+++ b/src/object.c
@@ -202,7 +202,13 @@ static OCI_TypeInfo * ObjectGetRealTypeInfo(OCI_TypeInfo *typinf, void *object)
/* first try to find it in list */
- parent = OcilibListFind(typinf->con->tinfs, (POCI_LIST_FIND)OcilibObjectTypeInfoFind, tdo);
+ LOCK_LIST
+ (
+ typinf->con->tinfs,
+ {
+ parent = OcilibListFind(typinf->con->tinfs, (POCI_LIST_FIND)OcilibObjectTypeInfoFind, tdo);
+ }
+ )
/* if found, it will be assigned in the cleanup part */
diff --git a/src/oci/api.h b/src/oci/api.h
index 8de420d6..f764214e 100644
--- a/src/oci/api.h
+++ b/src/oci/api.h
@@ -1097,6 +1097,22 @@ typedef sword (*OCITYPEBYREF)
OCIType **tdo
);
+typedef sword (*OCITYPEBYNAME)
+(
+ OCIEnv *env,
+ OCIError *err,
+ const OCISvcCtx *svc,
+ const oratext* schema_name,
+ ub4 s_length,
+ const oratext *type_name,
+ ub4 t_length,
+ const oratext *version_name,
+ ub4 v_length,
+ OCIDuration pin_duration,
+ OCITypeGetOpt get_option,
+ OCIType **tdo
+);
+
typedef sword (*OCIOBJECTNEW)
(
OCIEnv *env,
diff --git a/src/pool.c b/src/pool.c
index a5b276cf..832eb0e6 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -169,7 +169,14 @@ OCI_Pool * OcilibPoolCreate
/* create pool object */
- pool = OcilibListAppend(Env.pools, sizeof(*pool));
+ LOCK_LIST
+ (
+ Env.pools,
+ {
+ pool = OcilibListAppend(Env.pools, sizeof(*pool));
+ }
+ )
+
CHECK_NULL(pool)
pool->mode = mode;
@@ -388,8 +395,15 @@ boolean OcilibPoolFree
CHECK_PTR(OCI_IPC_POOL, pool)
- OcilibPoolDispose(pool);
- OcilibListRemove(Env.pools, pool);
+ LOCK_LIST
+ (
+ Env.pools,
+ {
+ OcilibListRemove(Env.pools, pool);
+ }
+ )
+
+ CHECK(OcilibPoolDispose(pool))
FREE(pool)
diff --git a/src/resultset.c b/src/resultset.c
index 882e1b4d..82b4f84e 100644
--- a/src/resultset.c
+++ b/src/resultset.c
@@ -1855,7 +1855,7 @@ const otext * OcilibResultsetGetString
{
OCI_Error *err = OcilibErrorGet(TRUE, TRUE);
- unsigned int bufsize = OCI_SIZE_TMP_CVT;
+ unsigned int buffer_size = OCI_SIZE_TMP_CVT;
unsigned int data_size = 0;
switch (def->col.datatype)
@@ -1882,8 +1882,9 @@ const otext * OcilibResultsetGetString
}
case OCI_CDT_RAW:
{
- data = OcilibDefineGetData(def);
- data_size = ((ub2*)def->buf.lens)[def->rs->row_cur - 1];
+ data = OcilibDefineGetData(def);
+ data_size = ((ub2*)def->buf.lens)[def->rs->row_cur - 1];
+ buffer_size = data_size * 2;
break;
}
case OCI_CDT_REF:
@@ -1897,12 +1898,12 @@ const otext * OcilibResultsetGetString
if (lg)
{
- bufsize = OcilibLongGetSize(lg);
+ buffer_size = OcilibLongGetSize(lg);
if (OCI_BLONG == def->col.subtype)
{
/* here we have binary long, it will be output in hexadecimal */
- bufsize *= 2;
+ buffer_size *= 2;
}
}
@@ -1916,12 +1917,12 @@ const otext * OcilibResultsetGetString
if (lob)
{
- bufsize = (unsigned int)OcilibLobGetLength(lob);
+ buffer_size = (unsigned int)OcilibLobGetLength(lob);
if (OCI_BLOB == def->col.subtype)
{
/* here we have binary blob, it will be output in hexadecimal */
- bufsize *= 2;
+ buffer_size *= 2;
}
}
@@ -1936,12 +1937,12 @@ const otext * OcilibResultsetGetString
if (file)
{
- bufsize = (unsigned int) ostrlen(OTEXT("/"));
+ buffer_size = (unsigned int) ostrlen(OTEXT("/"));
OcilibFileGetInfo(file);
- bufsize += (unsigned int) (file->dir ? ostrlen(file->dir) : 0);
- bufsize += (unsigned int) (file->name ? ostrlen(file->name) : 0);
+ buffer_size += (unsigned int) (file->dir ? ostrlen(file->dir) : 0);
+ buffer_size += (unsigned int) (file->name ? ostrlen(file->name) : 0);
}
data = file;
@@ -1953,7 +1954,7 @@ const otext * OcilibResultsetGetString
if (obj)
{
- CHECK(OcilibObjectToString(obj, &bufsize, NULL))
+ CHECK(OcilibObjectToString(obj, &buffer_size, NULL))
}
data = obj;
@@ -1965,7 +1966,7 @@ const otext * OcilibResultsetGetString
if (coll)
{
- CHECK(OcilibCollectionToString(coll, &bufsize, NULL))
+ CHECK(OcilibCollectionToString(coll, &buffer_size, NULL))
}
data = coll;
@@ -1977,7 +1978,7 @@ const otext * OcilibResultsetGetString
if (stmt && stmt->sql)
{
- bufsize = (unsigned int) ostrlen(stmt->sql);
+ buffer_size = (unsigned int) ostrlen(stmt->sql);
}
data = stmt;
@@ -1991,7 +1992,7 @@ const otext * OcilibResultsetGetString
CHECK(NULL == err || OCI_UNKNOWN == err->type)
- CHECK(OcilibStringRequestBuffer(&def->buf.tmpbuf, &def->buf.tmpsize, bufsize))
+ CHECK(OcilibStringRequestBuffer(&def->buf.tmpbuf, &def->buf.tmpsize, buffer_size))
CHECK(OcilibStringGetFromType(rs->stmt->con, &def->col, data, data_size,
def->buf.tmpbuf, def->buf.tmpsize, FALSE))
diff --git a/src/statement.c b/src/statement.c
index 23a4c64f..b2fc10c8 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -1520,7 +1520,16 @@ OCI_Statement * OcilibStatementCreate
/* create statement object */
- OCI_Statement *stmt = OcilibListAppend(con->stmts, sizeof(*stmt));
+ OCI_Statement *stmt = NULL;
+
+ LOCK_LIST
+ (
+ con->stmts,
+ {
+ stmt = OcilibListAppend(con->stmts, sizeof(*stmt));
+ }
+ )
+
CHECK_NULL(stmt)
SET_RETVAL(OcilibStatementInitialize(con, (OCI_Statement*)stmt, NULL, FALSE, NULL))
@@ -1546,8 +1555,15 @@ boolean OcilibStatementFree
CHECK_PTR(OCI_IPC_STATEMENT, stmt)
CHECK_OBJECT_FETCHED(stmt)
+ LOCK_LIST
+ (
+ stmt->con->stmts,
+ {
+ OcilibListRemove(stmt->con->stmts, stmt);
+ }
+ )
+
OcilibStatementDispose(stmt);
- OcilibListRemove(stmt->con->stmts, stmt);
FREE(stmt)
diff --git a/src/subscription.c b/src/subscription.c
index 73c7bb96..d0991ef8 100644
--- a/src/subscription.c
+++ b/src/subscription.c
@@ -172,7 +172,14 @@ OCI_Subscription * OcilibSubscriptionRegister
/* create subscription object */
- sub = OcilibListAppend(Env.subs, sizeof(*sub));
+ LOCK_LIST
+ (
+ Env.subs,
+ {
+ sub = OcilibListAppend(Env.subs, sizeof(*sub));
+ }
+ )
+
CHECK_NULL(sub)
/* allocate error handle */
@@ -326,8 +333,8 @@ OCI_Subscription * OcilibSubscriptionRegister
{
OcilibStringReleaseDBString(dbstr);
- OcilibSubscriptionDispose(sub);
- OcilibListRemove(Env.subs, sub);
+ OcilibSubscriptionUnregister(sub);
+
FREE(sub)
}
)
@@ -350,8 +357,15 @@ boolean OcilibSubscriptionUnregister
CHECK_PTR(OCI_IPC_NOTIFY, sub)
+ LOCK_LIST
+ (
+ Env.subs,
+ {
+ OcilibListRemove(Env.subs, sub);
+ }
+ )
+
OcilibSubscriptionDispose(sub);
- OcilibListRemove(Env.subs, sub);
FREE(sub);
diff --git a/src/transaction.c b/src/transaction.c
index 27dc4022..d35d2f19 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -86,7 +86,14 @@ OCI_Transaction * OcilibTransactionCreate
/* create transaction object */
- trans = OcilibListAppend(con->trsns, sizeof(*trans));
+ LOCK_LIST
+ (
+ con->trsns,
+ {
+ trans = OcilibListAppend(con->trsns, sizeof(*trans));
+ }
+ )
+
CHECK_NULL(trans)
trans->con = con;
@@ -141,11 +148,17 @@ boolean OcilibTransactionFree
CHECK_PTR(OCI_IPC_TRANSACTION, trans)
- OcilibTransactionDispose(trans);
-
/* remove transaction from internal list */
- OcilibListRemove(trans->con->trsns, trans);
+ LOCK_LIST
+ (
+ trans->con->trsns,
+ {
+ OcilibListRemove(trans->con->trsns, trans);
+ }
+ )
+
+ CHECK(OcilibTransactionDispose(trans))
FREE(trans)
diff --git a/src/typeinfo.c b/src/typeinfo.c
index ecf70422..882679da 100644
--- a/src/typeinfo.c
+++ b/src/typeinfo.c
@@ -24,6 +24,7 @@
#include "list.h"
#include "macros.h"
#include "memory.h"
+#include "mutex.h"
#include "stringutils.h"
static unsigned int TypeInfoTypeValues[] =
@@ -39,7 +40,7 @@ typedef struct TypeInfoFindParams
otext * schema;
otext * name;
} TypeInfoFindParams;
-
+
/* --------------------------------------------------------------------------------------------- *
* OcilibTypeInfoFind
* --------------------------------------------------------------------------------------------- */
@@ -54,6 +55,36 @@ static boolean OcilibTypeInfoFind(OCI_TypeInfo *typinf, TypeInfoFindParams *find
OcilibStringCaseCompare(typinf->schema, find_params->schema) == 0;
}
+/* --------------------------------------------------------------------------------------------- *
+ * OcilibTypeInfoFindOrCreate
+ * --------------------------------------------------------------------------------------------- */
+
+static OCI_TypeInfo* OcilibTypeInfoFindOrCreate(OCI_Connection* con, TypeInfoFindParams* p_find_params, boolean* p_is_created)
+{
+ *p_is_created = FALSE;
+
+ OCI_TypeInfo* typinf = OcilibListFind(con->tinfs, (POCI_LIST_FIND)OcilibTypeInfoFind, p_find_params);
+
+ if (NULL == typinf)
+ {
+ typinf = OcilibListAppend(con->tinfs, sizeof(OCI_TypeInfo));
+ if (typinf == NULL)
+ {
+ return typinf;
+ }
+
+ typinf->con = con;
+ typinf->name = OcilibStringDuplicate(p_find_params->name);
+ typinf->schema = OcilibStringDuplicate(p_find_params->schema);
+ typinf->struct_size = 0;
+ typinf->align = 0;
+
+ *p_is_created = TRUE;
+ }
+
+ return typinf;
+}
+
/* --------------------------------------------------------------------------------------------- *
* OcilibTypeInfoDispose
* --------------------------------------------------------------------------------------------- */
@@ -139,11 +170,17 @@ OCI_TypeInfo * OcilibTypeInfoGet
size_t max_chars = sizeof(buffer) / sizeof(otext) - 1;
dbtext* dbstr1 = NULL;
int dbsize1 = -1;
+ dbtext* dbstr2 = NULL;
+ int dbsize2 = -1;
+ dbtext* dbstr3 = NULL;
+ int dbsize3 = -1;
sb4 pbsp = 1;
otext obj_schema[OCI_SIZE_OBJ_NAME + 1];
otext obj_name[OCI_SIZE_OBJ_NAME + 1];
+ boolean is_created = FALSE;
+
CHECK_PTR(OCI_IPC_CONNECTION, con)
CHECK_PTR(OCI_IPC_STRING, name)
CHECK_ENUM_VALUE(type, TypeInfoTypeValues, OTEXT("Type"))
@@ -196,23 +233,22 @@ OCI_TypeInfo * OcilibTypeInfoGet
find_params.name = obj_name;
find_params.schema = obj_schema;
- typinf = OcilibListFind(con->tinfs, (POCI_LIST_FIND)OcilibTypeInfoFind, &find_params);
+ LOCK_LIST
+ (
+ con->tinfs,
+ {
+ typinf = OcilibTypeInfoFindOrCreate(con, &find_params, &is_created);
+ }
+ )
+
+ CHECK_NULL(typinf)
/* Not found, so create type object */
- if (NULL == typinf)
+ if (is_created)
{
- typinf = OcilibListAppend(con->tinfs, sizeof(OCI_TypeInfo));
- CHECK_NULL(typinf)
-
/* allocate describe handle */
-
- typinf->con = con;
- typinf->name = OcilibStringDuplicate(obj_name);
- typinf->schema = OcilibStringDuplicate(obj_schema);
- typinf->struct_size = 0;
- typinf->align = 0;
-
+
CHECK(OcilibMemoryAllocHandle(typinf->con->env, (dvoid **)(void *)&dschp, OCI_HTYPE_DESCRIBE))
/* perform describe */
@@ -242,17 +278,52 @@ OCI_TypeInfo * OcilibTypeInfoGet
dschp, &pbsp, sizeof(pbsp),
con->err
)
-
+
/* describe call */
- CHECK_OCI
- (
- con->err,
- OCIDescribeAny,
- con->cxt, con->err, (dvoid *) dbstr1,
- (ub4) dbsize1, OCI_OTYPE_NAME,
- OCI_DEFAULT, OCI_PTYPE_UNK, dschp
- )
+ if (OCI_TIF_TYPE == type)
+ {
+ OCIType* tdo = NULL;
+
+ /* for types, as OCIDescribeAny() doest not support some cases like SYS.RAW anymore
+ like on previous Oracle versions, let's use OCITypeByName()
+ */
+
+ dbstr2 = OcilibStringGetDBString(typinf->schema, &dbsize2);
+ dbstr3 = OcilibStringGetDBString(typinf->name, &dbsize3);
+
+ CHECK_OCI
+ (
+ con->err,
+ OCITypeByName,
+ con->env, con->err, con->cxt,
+ (CONST text*) dbstr2, dbsize2,
+ (CONST text*) dbstr3, dbsize3,
+ (text*)0, 0,
+ OCI_DURATION_SESSION, OCI_TYPEGET_ALL,
+ &tdo
+ )
+
+ CHECK_OCI
+ (
+ con->err,
+ OCIDescribeAny,
+ con->cxt, con->err, tdo,
+ (ub4) 0, OCI_OTYPE_PTR,
+ OCI_DEFAULT, OCI_PTYPE_UNK, dschp
+ )
+ }
+ else
+ {
+ CHECK_OCI
+ (
+ con->err,
+ OCIDescribeAny,
+ con->cxt, con->err, (dvoid*)dbstr1,
+ (ub4)dbsize1, OCI_OTYPE_NAME,
+ OCI_DEFAULT, OCI_PTYPE_UNK, dschp
+ )
+ }
/* get parameter handle */
@@ -415,7 +486,10 @@ OCI_TypeInfo * OcilibTypeInfoGet
}
default:
{
- THROW(OcilibExceptionDatatypeNotSupported, typinf->typecode)
+ if (!pdt)
+ {
+ THROW(OcilibExceptionDatatypeNotSupported, typinf->typecode)
+ }
break;
}
}
@@ -528,15 +602,11 @@ OCI_TypeInfo * OcilibTypeInfoGet
}
}
}
- }
-
- /* free describe handle */
- OcilibMemoryFreeHandle(dschp, OCI_HTYPE_DESCRIBE);
+ /* free describe handle */
- /* increment type info reference counter on success */
-
- typinf->refcount++;
+ OcilibMemoryFreeHandle(dschp, OCI_HTYPE_DESCRIBE);
+ }
/* type checking sanity checks */
@@ -550,6 +620,8 @@ OCI_TypeInfo * OcilibTypeInfoGet
CLEANUP_AND_EXIT_FUNC
(
OcilibStringReleaseDBString(dbstr1);
+ OcilibStringReleaseDBString(dbstr2);
+ OcilibStringReleaseDBString(dbstr3);
/* free temporary strings */
@@ -585,16 +657,6 @@ boolean OcilibTypeInfoFree
CHECK_PTR(OCI_IPC_TYPE_INFO, typinf)
- typinf->refcount--;
-
- if (typinf->refcount == 0)
- {
- OcilibListRemove(typinf->con->tinfs, typinf);
- OcilibTypeInfoDispose(typinf);
-
- FREE(typinf)
- }
-
SET_SUCCESS()
EXIT_FUNC()
diff --git a/src/types.h b/src/types.h
index 2bdd3637..f2f5c378 100644
--- a/src/types.h
+++ b/src/types.h
@@ -724,7 +724,6 @@ struct OCI_TypeInfo
ub2 typecode; /* Oracle type code */
ub2 colcode; /* Oracle collection code */
ub2 nb_cols; /* number of columns */
- ub2 refcount; /* reference counter */
ub1 is_final; /* is is a virtual type that can be inherited ? */
};
diff --git a/tests/TestCollection.cpp b/tests/TestCollection.cpp
index d3d0534f..97ff04c1 100644
--- a/tests/TestCollection.cpp
+++ b/tests/TestCollection.cpp
@@ -128,7 +128,7 @@ TEST(TestCollection, SelectVarray)
ExecDML(OTEXT("insert into SelectVarray_Table values('category 1', SelectVarray_Coll(SelectVarray_Type(1, 'name 1'), SelectVarray_Type(2, 'name 2')))"));
ExecDML(OTEXT("insert into SelectVarray_Table values('category 2', SelectVarray_Coll(SelectVarray_Type(3, 'name 3'), SelectVarray_Type(4, 'name 4')))"));
- SelectCollection(OTEXT("SELECT * from SelectVarray_Table"));
+ SelectCollection(OTEXT("SELECT * from SelectVarray_Table order by category"));
ExecDML(OTEXT("drop table SelectVarray_Table"));
ExecDML(OTEXT("drop type SelectVarray_Coll"));
@@ -144,7 +144,7 @@ TEST(TestCollection, SelectNestedTable)
ExecDML(OTEXT("insert into SelectNestedTable_Table values('category 1', SelectNestedTable_Coll(SelectNestedTable_Type(1, 'name 1'), SelectNestedTable_Type(2, 'name 2')))"));
ExecDML(OTEXT("insert into SelectNestedTable_Table values('category 2', SelectNestedTable_Coll(SelectNestedTable_Type(3, 'name 3'), SelectNestedTable_Type(4, 'name 4')))"));
- SelectCollection(OTEXT("SELECT * from SelectNestedTable_Table"));
+ SelectCollection(OTEXT("SELECT * from SelectNestedTable_Table order by category"));
ExecDML(OTEXT("drop table SelectNestedTable_Table"));
ExecDML(OTEXT("drop type SelectNestedTable_Coll"));
diff --git a/tests/TestDirectPath.cpp b/tests/TestDirectPath.cpp
new file mode 100644
index 00000000..ab1c7da7
--- /dev/null
+++ b/tests/TestDirectPath.cpp
@@ -0,0 +1,381 @@
+#include "ocilib_tests.h"
+#include "ocilib.hpp"
+
+using namespace ocilib;
+
+enum DirectPathErrorTestMode
+{
+ Default = 0,
+ InsufficientBufferError = 1,
+ ConversionError = 2,
+ LoadError = 4,
+ ForceLoad = 8
+};
+
+class TestDirectPathErrorModes : public ::testing::TestWithParam {};
+
+std::vector DirectPathErrorTestModes
+{
+ DirectPathErrorTestMode::Default,
+ // DirectPathErrorTestMode::InsufficientBufferError,
+ // DirectPathErrorTestMode::InsufficientBufferError | DirectPathErrorTestMode::ForceLoad,
+ DirectPathErrorTestMode::ConversionError,
+ DirectPathErrorTestMode::ConversionError | DirectPathErrorTestMode::ForceLoad,
+ DirectPathErrorTestMode::LoadError,
+ DirectPathErrorTestMode::LoadError | DirectPathErrorTestMode::ForceLoad
+};
+TEST(TestDirectPath, Simple)
+{
+ auto constexpr ArraySize = 10u;
+ auto constexpr LoadSize = 10u;
+ auto constexpr ColCount = 3u;
+ auto constexpr ColSize1 = 20u;
+ auto constexpr ColSize2 = 30u;
+ auto constexpr ColSize3 = 8u;
+
+ ExecDML(OTEXT("create table TestDirectPathTable1 (val_int int, val_str varchar2(50), val_date date)"));
+ ExecDML(OTEXT("truncate table TestDirectPathTable1"));
+
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ otext val1[ColSize1 + 1];
+ otext val2[ColSize2 + 1];
+ otext val3[ColSize3 + 1];
+
+ int i = 0, j = 0, nb_rows = ArraySize;
+ boolean res = TRUE;
+
+ const auto table = OCI_TypeInfoGet(conn, OTEXT("TestDirectPathTable1"), OCI_TIF_TABLE);
+ ASSERT_NE(nullptr, table);
+
+ const auto dp = OCI_DirPathCreate(table, NULL, ColCount, nb_rows);
+ ASSERT_NE(nullptr, dp);
+
+ /* optional attributes to set */
+
+ ASSERT_TRUE(OCI_DirPathSetBufferSize(dp, 64000));
+ ASSERT_TRUE(OCI_DirPathSetNoLog(dp, TRUE));
+ ASSERT_TRUE(OCI_DirPathSetParallel(dp, TRUE));
+
+ /* describe the target table */
+
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 1, OTEXT("val_int"), ColSize1, nullptr));
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 2, OTEXT("val_str"), ColSize2, nullptr));
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 3, OTEXT("val_date"), ColSize3, OTEXT("YYYYMMDD")));
+
+ /* prepare the load */
+
+ ASSERT_TRUE(OCI_DirPathPrepare(dp));
+
+ nb_rows = OCI_DirPathGetMaxRows(dp);
+ ASSERT_NE(0, nb_rows);
+
+ for (i = 0; i < LoadSize; i++)
+ {
+ ASSERT_TRUE(OCI_DirPathReset(dp));
+
+ for (j = 1; j <= nb_rows; j++)
+ {
+ /* fill test values */
+
+ osprintf(val1, "%04d", i + (i * 100));
+ osprintf(val2, "value %05d", j + (i * 100));
+ osprintf(val3, "%04d%02d%02d", (j % 23) + 1 + 2000, (j % 11) + 1, (j % 23) + 1);
+
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 1, val1, (unsigned int)ostrlen(val1), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 2, val2, (unsigned int)ostrlen(val2), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 3, val3, (unsigned int)ostrlen(val3), TRUE));
+ }
+
+ /* load data to the server */
+
+ while (res)
+ {
+ int state = OCI_DirPathConvert(dp);
+
+ if ((state == OCI_DPR_FULL) || (state == OCI_DPR_COMPLETE))
+ res = OCI_DirPathLoad(dp);
+
+ if (state == OCI_DPR_COMPLETE)
+ break;
+
+ if (state == OCI_DPR_ERROR)
+ break;
+ }
+ }
+
+ /* commits changes */
+
+ ASSERT_TRUE(OCI_DirPathFinish(dp));
+
+ ASSERT_EQ(ArraySize * LoadSize, OCI_DirPathGetRowCount(dp));
+
+ /* cleanup */
+
+ ASSERT_TRUE(OCI_DirPathFree(dp));
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+
+ ExecDML(OTEXT("drop table TestDirectPathTable1"));
+}
+
+void CheckAllErrorWorkflows(OCI_Connection* con, OCI_DirPath* dp, int expected, int result, char* str)
+{
+ ASSERT_EQ(expected, result) << str;
+}
+
+void CheckAllErrorWorkflowsData(OCI_Connection* con, OCI_DirPath* dp, boolean gen_conv_error, boolean gen_load_error, boolean force)
+{
+ auto constexpr ArraySize = 10u;
+ auto constexpr LoadSize = 10u;
+ auto constexpr TotalCount = ArraySize * LoadSize;
+ auto constexpr ErrCount = 2u;
+
+ int nb_rows = 0;
+
+ OCI_Immediate(con, "select count(*) from TestDirectPathTable2", OCI_ARG_INT, &nb_rows);
+ CheckAllErrorWorkflows(con, dp, ((gen_conv_error && force) || gen_load_error) ? (TotalCount - ErrCount) : TotalCount, nb_rows, "total rows in database");
+
+ OCI_ImmediateFmt(con, "select count(distinct val_int) from TestDirectPathTable2", OCI_ARG_INT, &nb_rows);
+ CheckAllErrorWorkflows(con, dp, ((gen_conv_error && force) || gen_load_error) ? (TotalCount - ErrCount) : TotalCount, nb_rows, "distinct rows in database");
+}
+
+TEST_P(TestDirectPathErrorModes, AllErrorWorkflows)
+{
+ auto constexpr ArraySize = 10u;
+ auto constexpr LoadSize = 10u;
+ auto constexpr ColCount = 3u;
+ auto constexpr ColSize1 = 20u;
+ auto constexpr ColSize2 = 30u;
+ auto constexpr ColSize3 = 8u;
+ auto constexpr ErrCount = 2u;
+ auto constexpr TotalCount = ArraySize * LoadSize;
+
+ auto mode = GetParam();
+
+ ExecDML(OTEXT("create table TestDirectPathTable2(val_int int not null, val_str varchar2(30), val_date date) partition by range(val_int) ( partition test_dp_1 values less than (501), partition test_dp_2 values less than (1001))"));
+ ExecDML(OTEXT("truncate table TestDirectPathTable2"));
+
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ otext val1[ColSize1 + 1];
+ otext val2[ColSize2 + 1];
+ otext val3[ColSize3 + 1];
+
+ int i = 0, j = 0, nb_rows = ArraySize;
+ boolean res = TRUE;
+
+ const auto table = OCI_TypeInfoGet(conn, OTEXT("TestDirectPathTable2"), OCI_TIF_TABLE);
+ ASSERT_NE(nullptr, table);
+
+ const auto dp = OCI_DirPathCreate(table, NULL, ColCount, nb_rows);
+ ASSERT_NE(nullptr, dp);
+
+ bool force = mode & DirectPathErrorTestMode::ForceLoad;
+ bool gen_buffer_error = mode & DirectPathErrorTestMode::InsufficientBufferError;
+ bool gen_conv_error = mode & DirectPathErrorTestMode::ConversionError;
+ bool gen_load_error = mode & DirectPathErrorTestMode::LoadError;
+
+ /* optional attributes to set */
+
+ ASSERT_TRUE(OCI_DirPathSetBufferSize(dp, gen_buffer_error ? 100 : 64000));
+ ASSERT_TRUE(OCI_DirPathSetConvertMode(dp, force ? OCI_DCM_FORCE : OCI_DCM_DEFAULT));
+
+ /* describe the target table */
+
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 1, OTEXT("val_int"), ColSize1, nullptr));
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 2, OTEXT("val_str"), ColSize2, nullptr));
+ ASSERT_TRUE(OCI_DirPathSetColumn(dp, 3, OTEXT("val_date"), ColSize3, OTEXT("YYYYMMDD")));
+
+ /* prepare the load */
+
+ ASSERT_TRUE(OCI_DirPathPrepare(dp));
+
+ nb_rows = OCI_DirPathGetMaxRows(dp);
+ ASSERT_NE(0, nb_rows);
+
+ for (i = 0; i < LoadSize; i++)
+ {
+ ASSERT_TRUE(OCI_DirPathReset(dp));
+
+ /* set the values for the current load iteration */
+ for (j = 1; j <= nb_rows; j++)
+ {
+ /* conversion and loading errors are performed on the first load for rows 5 and 8 */
+ if (i == 0 && (j == 5 || j == 8) && gen_conv_error)
+ {
+ /* generate a conversion error - not null clause violation on column val_int */
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 1, NULL, 0, TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 2, NULL, 0, TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 3, NULL, 0, TRUE));
+ }
+ else if (i == 0 && (j == 5 || j == 8) && gen_load_error)
+ {
+ /* generate a load error - if partitioning available, insert value out of declared partitions ranges */
+ osprintf(val1, OTEXT("%04d"), 2500);
+ osprintf(val2, OTEXT("value %05d"), j + (i * ArraySize));
+ osprintf(val3, OTEXT("%04d%02d%02d"), (j % 23) + 1 + 2000, (j % 11) + 1, (j % 23) + 1);
+
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 1, val1, (unsigned int)ostrlen(val1), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 2, val2, (unsigned int)ostrlen(val2), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 3, val3, (unsigned int)ostrlen(val3), TRUE));
+ }
+ else
+ {
+ /* default loading */
+ osprintf(val1, OTEXT("%04d"), j + (i * ArraySize));
+ osprintf(val2, OTEXT("value %05d"), j + (i * ArraySize));
+ osprintf(val3, OTEXT("%04d%02d%02d"), (j % 23) + 1 + 2000, (j % 11) + 1, (j % 23) + 1);
+
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 1, val1, (unsigned int)ostrlen(val1), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 2, val2, (unsigned int)ostrlen(val2), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 3, val3, (unsigned int)ostrlen(val3), TRUE));
+ }
+ }
+
+ /* load data to the server */
+ while (TRUE)
+ {
+ int nb_conv = 0;
+ int nb_error = 0;
+
+ /* convert data*/
+ int state = OCI_DirPathConvert(dp);
+
+ /* retrieve the number of converted rows */
+ nb_conv += OCI_DirPathGetAffectedRows(dp);
+
+ /* conversion or loading errors are performed on the first load */
+ if (i == 0)
+ {
+ if (gen_conv_error && !force && state == OCI_DPR_ERROR)
+ {
+ /* on conversion error in default mode, correct values for next conversion */
+ int row = OCI_DirPathGetErrorRow(dp);
+
+ osprintf(val1, OTEXT("%04d"), row);
+ osprintf(val2, OTEXT("value %05d"), row);
+ osprintf(val3, OTEXT("%04d%02d%02d"), (row % 23) + 1 + 2000, (row % 11) + 1, (row % 23) + 1);
+
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, row, 1, val1, (unsigned int)ostrlen(val1), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, row, 2, val2, (unsigned int)ostrlen(val2), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, row, 3, val3, (unsigned int)ostrlen(val3), TRUE));
+
+ nb_conv = 0;
+ }
+
+ if (gen_conv_error && force && state == OCI_DPR_COMPLETE)
+ {
+ /* on conversion error in force mode, check we got the expected errors*/
+ int err_row = OCI_DirPathGetErrorRow(dp);
+ int err_col = OCI_DirPathGetErrorColumn(dp);
+
+ while (err_row != 0)
+ {
+ CheckAllErrorWorkflows(conn, dp, nb_error == 0 ? 5 : 8, err_row, "erred converted row index (force mode)");
+ CheckAllErrorWorkflows(conn, dp, err_col, 1, "erred column index in force mode");
+
+ err_row = OCI_DirPathGetErrorRow(dp);
+ err_col = OCI_DirPathGetErrorColumn(dp);
+
+ nb_error++;
+ }
+
+ CheckAllErrorWorkflows(conn, dp, ErrCount, nb_error, "conversion errors in force mode");
+ }
+ }
+
+ if (state == OCI_DPR_FULL)
+ {
+ /* buffer is too small - load the stream */
+
+ state = OCI_DirPathLoad(dp);
+
+ /* as the stream cannot accept all rows in one go,
+ we need to do conversion+load until all rows are processed */
+
+ while (nb_conv < ArraySize)
+ {
+ for (j = 1; j <= nb_rows - nb_conv; j++)
+ {
+ osprintf(val1, OTEXT("%04d"), (j + nb_conv) + (i * ArraySize));
+ osprintf(val2, OTEXT("value %05d"), (j + nb_conv) + (i * ArraySize));
+ osprintf(val3, OTEXT("%04d%02d%02d"), ((j + nb_conv) % 23) + 1 + 2000, ((j + nb_conv) % 11) + 1, ((j + nb_conv) % 23) + 1);
+
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 1, val1, (unsigned int)ostrlen(val1), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 2, val2, (unsigned int)ostrlen(val2), TRUE));
+ ASSERT_TRUE(OCI_DirPathSetEntry(dp, j, 3, val3, (unsigned int)ostrlen(val3), TRUE));
+ }
+
+ /* convert again */
+
+ state = OCI_DirPathConvert(dp);
+
+ nb_conv = OCI_DirPathGetAffectedRows(dp);
+
+ /* load again */
+
+ state = OCI_DirPathLoad(dp);
+ }
+
+ break;
+ }
+ else if (state == OCI_DPR_COMPLETE)
+ {
+ /* conversion is complete */
+
+ CheckAllErrorWorkflows(conn, dp, (gen_conv_error && i == 0 && force) ? (ArraySize - ErrCount) : ArraySize, nb_conv, "converted rows");
+
+ state = OCI_DirPathLoad(dp);
+
+ if (gen_load_error && i == 0)
+ {
+ /* on loading error in force mode, check we got the expected errors*/
+ int err_row = OCI_DirPathGetErrorRow(dp);
+
+ while (err_row != 0)
+ {
+ CheckAllErrorWorkflows(conn, dp, nb_error == 0 ? 5 : 8, err_row, "erred loaded row index");
+
+ err_row = OCI_DirPathGetErrorRow(dp);
+
+ nb_error++;
+ }
+
+ CheckAllErrorWorkflows(conn, dp, ErrCount, nb_error, "loading errors in force mode");
+ }
+
+ CheckAllErrorWorkflows(conn, dp, (gen_load_error && i == 0) ? nb_conv - ErrCount : nb_conv, OCI_DirPathGetAffectedRows(dp), "loaded rows");
+
+ break;
+ }
+ }
+ }
+
+ /* commits changes */
+
+ ASSERT_TRUE(OCI_DirPathFinish(dp));
+
+ /* verify loading results */
+ CheckAllErrorWorkflows(conn, dp, ((gen_conv_error&& force) || gen_load_error) ? (TotalCount - ErrCount) : TotalCount, OCI_DirPathGetRowCount(dp), "total loaded rows");
+
+ /* check data integrity in database */
+ CheckAllErrorWorkflowsData(conn, dp, gen_conv_error, gen_load_error, force);
+
+ /* cleanup */
+
+ ASSERT_TRUE(OCI_DirPathFree(dp));
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+
+ ExecDML(OTEXT("drop table TestDirectPathTable2"));
+}
+
+INSTANTIATE_TEST_CASE_P(TestDirectPathErrorModes, TestDirectPathErrorModes, ::testing::ValuesIn(DirectPathErrorTestModes));
+
diff --git a/tests/TestQueue.cpp b/tests/TestQueue.cpp
index 2bb3929c..75811d62 100644
--- a/tests/TestQueue.cpp
+++ b/tests/TestQueue.cpp
@@ -182,4 +182,211 @@ TEST(TestQueue, MessageWithConsumers)
ASSERT_TRUE(OCI_Cleanup());
ExecDML(OTEXT("drop type TestQueueMessageWithConsumersType"));
+}
+
+TEST(TestQueue, SingleRawMessage)
+{
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ // no check - for cleanup
+ OCI_QueueStop(conn, OTEXT("TestQueueSingleRawMessage"), TRUE, TRUE, FALSE);
+ OCI_QueueDrop(conn, OTEXT("TestQueueSingleRawMessage"));
+ OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleRawMessageTable"), TRUE);
+ // end no check
+
+ ASSERT_TRUE(OCI_QueueTableCreate(conn, OTEXT("TestQueueSingleRawMessageTable"), OTEXT("RAW"), NULL, NULL, FALSE, OCI_AGM_NONE, NULL, 0, 0, NULL));
+ ASSERT_TRUE(OCI_QueueCreate(conn, OTEXT("TestQueueSingleRawMessage"), OTEXT("TestQueueSingleRawMessageTable"), OCI_AQT_NORMAL, 0, 0, 0, FALSE, NULL));
+ ASSERT_TRUE(OCI_QueueStart(conn, OTEXT("TestQueueSingleRawMessage"), TRUE, TRUE));
+
+ const auto inf = OCI_TypeInfoGet(conn, OTEXT("SYS.RAW"), OCI_TIF_TYPE);
+ ASSERT_NE(nullptr, inf);
+
+ const auto enq = OCI_EnqueueCreate(inf, OTEXT("TestQueueSingleRawMessage"));
+ ASSERT_NE(nullptr, enq);
+
+ const auto deq = OCI_DequeueCreate(inf, OTEXT("TestQueueSingleRawMessage"));
+ ASSERT_NE(nullptr, deq);
+
+ const auto msg_in = OCI_MsgCreate(inf);
+ ASSERT_NE(nullptr, msg_in);
+
+ const auto byteBring = ostring(OTEXT("123456789"));
+ ASSERT_TRUE(OCI_MsgSetRaw(msg_in, static_cast(byteBring.data()), static_cast(byteBring.size())));
+
+ ASSERT_TRUE(OCI_EnqueuePut(enq, msg_in));
+
+ ASSERT_TRUE(OCI_MsgFree(msg_in));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ const auto msg_out = OCI_DequeueGet(deq);
+ ASSERT_NE(nullptr, msg_out);
+
+ otext buffer[100] = OTEXT("");
+ unsigned int size = 100;
+ ASSERT_TRUE(OCI_MsgGetRaw(msg_out, static_cast(buffer), &size));
+
+ ASSERT_EQ(byteBring, ostring(buffer));
+ ASSERT_EQ(byteBring.size(), size);
+
+ ASSERT_TRUE(OCI_EnqueueFree(enq));
+ ASSERT_TRUE(OCI_DequeueFree(deq));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ ASSERT_TRUE(OCI_QueueStop(conn, OTEXT("TestQueueSingleRawMessage"), TRUE, TRUE, FALSE));
+ ASSERT_TRUE(OCI_QueueDrop(conn, OTEXT("TestQueueSingleRawMessage")));
+ ASSERT_TRUE(OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleRawMessageTable"), TRUE));
+
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+}
+
+TEST(TestQueue, SingleRawMessageEmptyPayload)
+{
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ // no check - for cleanup
+ OCI_QueueStop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload"), TRUE, TRUE, FALSE);
+ OCI_QueueDrop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload"));
+ OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayloadTable"), TRUE);
+ // end no check
+
+ ASSERT_TRUE(OCI_QueueTableCreate(conn, OTEXT("TestQueueSingleRawMessageEmptyPayloadTable"), OTEXT("RAW"), NULL, NULL, FALSE, OCI_AGM_NONE, NULL, 0, 0, NULL));
+ ASSERT_TRUE(OCI_QueueCreate(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload"), OTEXT("TestQueueSingleRawMessageEmptyPayloadTable"), OCI_AQT_NORMAL, 0, 0, 0, FALSE, NULL));
+ ASSERT_TRUE(OCI_QueueStart(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload"), TRUE, TRUE));
+
+ const auto inf = OCI_TypeInfoGet(conn, OTEXT("SYS.RAW"), OCI_TIF_TYPE);
+ ASSERT_NE(nullptr, inf);
+
+ const auto enq = OCI_EnqueueCreate(inf, OTEXT("TestQueueSingleRawMessageEmptyPayload"));
+ ASSERT_NE(nullptr, enq);
+
+ const auto deq = OCI_DequeueCreate(inf, OTEXT("TestQueueSingleRawMessageEmptyPayload"));
+ ASSERT_NE(nullptr, deq);
+
+ const auto msg_in = OCI_MsgCreate(inf);
+ ASSERT_NE(nullptr, msg_in);
+
+ const auto byteBring = ostring(OTEXT("123456789"));
+ ASSERT_TRUE(OCI_MsgSetOriginalID(msg_in, static_cast(byteBring.data()), static_cast(byteBring.size())));
+
+ ASSERT_TRUE(OCI_EnqueuePut(enq, msg_in));
+
+ ASSERT_TRUE(OCI_MsgFree(msg_in));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ const auto msg_out = OCI_DequeueGet(deq);
+ ASSERT_NE(nullptr, msg_out);
+
+ otext buffer[100] = OTEXT("");
+ unsigned int size = 100;
+ ASSERT_TRUE(OCI_MsgGetOriginalID(msg_out, static_cast(buffer), &size));
+
+ ASSERT_EQ(byteBring, ostring(buffer));
+ ASSERT_EQ(byteBring.size(), size);
+
+ otext payload[100] = OTEXT("");
+ unsigned int payloadSize = 100;
+ ASSERT_TRUE(OCI_MsgGetRaw(msg_out, static_cast(buffer), &payloadSize));
+ ASSERT_EQ(0, payloadSize);
+
+ ASSERT_TRUE(OCI_EnqueueFree(enq));
+ ASSERT_TRUE(OCI_DequeueFree(deq));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ ASSERT_TRUE(OCI_QueueStop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload"), TRUE, TRUE, FALSE));
+ ASSERT_TRUE(OCI_QueueDrop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayload")));
+ ASSERT_TRUE(OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleRawMessageEmptyPayloadTable"), TRUE));
+
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+}
+
+
+TEST(TestQueue, SingleMessageEmptyPayload)
+{
+ ExecDML(OTEXT("create type TestQueueSingleMessageEmptyPayloadType as object(title varchar2(50), content varchar2(100))"));
+
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ // no check - for cleanup
+ OCI_QueueStop(conn, OTEXT("TestQueueSingleMessageEmptyPayload"), TRUE, TRUE, FALSE);
+ OCI_QueueDrop(conn, OTEXT("TestQueueSingleMessageEmptyPayload"));
+ OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleMessageEmptyPayloadTable"), TRUE);
+ // end no check
+
+ ASSERT_TRUE(OCI_QueueTableCreate(conn, OTEXT("TestQueueSingleMessageEmptyPayloadTable"), OTEXT("TestQueueSingleMessageEmptyPayloadType"), NULL, NULL, FALSE, OCI_AGM_NONE, NULL, 0, 0, NULL));
+ ASSERT_TRUE(OCI_QueueCreate(conn, OTEXT("TestQueueSingleMessageEmptyPayload"), OTEXT("TestQueueSingleMessageEmptyPayloadTable"), OCI_AQT_NORMAL, 0, 0, 0, FALSE, NULL));
+ ASSERT_TRUE(OCI_QueueStart(conn, OTEXT("TestQueueSingleMessageEmptyPayload"), TRUE, TRUE));
+
+ const auto inf = OCI_TypeInfoGet(conn, OTEXT("TestQueueSingleMessageEmptyPayloadType"), OCI_TIF_TYPE);
+ ASSERT_NE(nullptr, inf);
+
+ const auto enq = OCI_EnqueueCreate(inf, OTEXT("TestQueueSingleMessageEmptyPayload"));
+ ASSERT_NE(nullptr, enq);
+
+ const auto deq = OCI_DequeueCreate(inf, OTEXT("TestQueueSingleMessageEmptyPayload"));
+ ASSERT_NE(nullptr, deq);
+
+ const auto msg_in = OCI_MsgCreate(inf);
+ ASSERT_NE(nullptr, msg_in);
+
+ const auto byteBring = ostring(OTEXT("123456789"));
+ ASSERT_TRUE(OCI_MsgSetOriginalID(msg_in, static_cast(byteBring.data()), static_cast(byteBring.size())));
+
+ ASSERT_TRUE(OCI_EnqueuePut(enq, msg_in));
+
+ otext msgIDIn[100] = OTEXT("");
+ unsigned int sizeMsgIDIn = 100;
+ ASSERT_TRUE(OCI_MsgGetID(msg_in, static_cast(msgIDIn), &sizeMsgIDIn));
+
+ ASSERT_TRUE(OCI_MsgFree(msg_in));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ const auto msg_out = OCI_DequeueGet(deq);
+ ASSERT_NE(nullptr, msg_out);
+
+ otext buffer[100] = OTEXT("");
+ unsigned int sizeBuffer = 100;
+ ASSERT_TRUE(OCI_MsgGetOriginalID(msg_out, static_cast(buffer), &sizeBuffer));
+
+ ASSERT_EQ(byteBring, ostring(buffer));
+ ASSERT_EQ(byteBring.size(), sizeBuffer);
+
+ otext msgIDOut[100] = OTEXT("");
+ unsigned int sizeMsgIDOut = 100;
+ ASSERT_TRUE(OCI_MsgGetID(msg_out, static_cast(msgIDOut), &sizeMsgIDOut));
+
+ ASSERT_EQ(ostring(msgIDIn), ostring(msgIDOut));
+ ASSERT_EQ(sizeMsgIDIn, sizeMsgIDOut);
+
+ const auto obj_out = OCI_MsgGetObject(msg_out);
+ ASSERT_EQ(nullptr, obj_out);
+
+ ASSERT_TRUE(OCI_EnqueueFree(enq));
+ ASSERT_TRUE(OCI_DequeueFree(deq));
+
+ ASSERT_TRUE(OCI_Commit(conn));
+
+ ASSERT_TRUE(OCI_QueueStop(conn, OTEXT("TestQueueSingleMessageEmptyPayload"), TRUE, TRUE, FALSE));
+ ASSERT_TRUE(OCI_QueueDrop(conn, OTEXT("TestQueueSingleMessageEmptyPayload")));
+ ASSERT_TRUE(OCI_QueueTableDrop(conn, OTEXT("TestQueueSingleMessageEmptyPayloadTable"), TRUE));
+
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+
+ ExecDML(OTEXT("drop type TestQueueSingleMessageEmptyPayloadType"));
}
\ No newline at end of file
diff --git a/tests/TestReportedIssuesCApi.cpp b/tests/TestReportedIssuesCApi.cpp
index 754fea07..1ea31219 100644
--- a/tests/TestReportedIssuesCApi.cpp
+++ b/tests/TestReportedIssuesCApi.cpp
@@ -253,5 +253,58 @@ TEST(ReportedIssuesCApi, Issue262)
ExecDML(OTEXT("DROP TABLE test_urowid"));
}
+TEST(ReportedIssuesCApi, Issue288)
+{
+ ExecDML(OTEXT("drop table TestIssue288"));
+ ExecDML(OTEXT("create table TestIssue288 (id number, val raw(2000))"));
+ ExecDML(OTEXT("insert into TestIssue288 "
+ "select lvl - 1, hextoraw(val) from "
+ "( "
+ " with l as(select level as lvl from dual connect by level < 102) "
+ " select lvl, listagg('FF', '') within group(order by null) as val "
+ " from l connect by level < lvl and prior lvl = lvl and prior sys_guid() is not null "
+ " group by lvl "
+ ") "
+ "where lvl > 1"));
+
+ ASSERT_TRUE(OCI_Initialize(nullptr, HOME, OCI_ENV_DEFAULT));
+
+ const auto conn = OCI_ConnectionCreate(DBS, USR, PWD, OCI_SESSION_DEFAULT);
+ ASSERT_NE(nullptr, conn);
+
+ const auto stmt = OCI_StatementCreate(conn);
+ ASSERT_NE(nullptr, stmt);
+
+ ASSERT_TRUE(OCI_ExecuteStmt(stmt, OTEXT("select id, val, length(val) from TestIssue288 order by id")));
+
+ auto rslt = OCI_GetResultset(stmt);
+ ASSERT_NE(nullptr, rslt);
+
+ auto counter = 0u;
+
+ while (OCI_FetchNext(rslt))
+ {
+ counter++;
+
+ auto id = OCI_GetInt(rslt, 1);
+ auto val = OCI_GetString(rslt, 2);
+ auto len = OCI_GetInt(rslt, 3);
+
+ ASSERT_NE(nullptr, val);
+
+ ASSERT_EQ(counter, id);
+ ASSERT_EQ(counter *2, len);
+ ASSERT_EQ(len, ostrlen(val));
+ }
+
+ ASSERT_EQ(100u, counter);
+
+ ASSERT_TRUE(OCI_StatementFree(stmt));
+ ASSERT_TRUE(OCI_ConnectionFree(conn));
+ ASSERT_TRUE(OCI_Cleanup());
+
+ ExecDML(OTEXT("drop table TestIssue288"));
+}
+
INSTANTIATE_TEST_CASE_P(ReportedIssuesCApi, ReportedIssues247, ::testing::ValuesIn(TimestampTypes));
diff --git a/tests/ocilib_tests.vcxproj b/tests/ocilib_tests.vcxproj
index 6b57e6dd..dda78d93 100644
--- a/tests/ocilib_tests.vcxproj
+++ b/tests/ocilib_tests.vcxproj
@@ -31,7 +31,7 @@
Win32Proj
10.0
Application
- v142
+ v143
Unicode
@@ -116,6 +116,7 @@
+
@@ -147,7 +148,7 @@
-
+
@@ -253,6 +254,6 @@
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
+
\ No newline at end of file
diff --git a/tests/ocilib_tests.vcxproj.filters b/tests/ocilib_tests.vcxproj.filters
index 0bc4517b..fb105ef2 100644
--- a/tests/ocilib_tests.vcxproj.filters
+++ b/tests/ocilib_tests.vcxproj.filters
@@ -217,6 +217,9 @@
Tests suite
+
+ Tests suite
+
diff --git a/tests/packages.config b/tests/packages.config
index cbf03ab8..dd871dad 100644
--- a/tests/packages.config
+++ b/tests/packages.config
@@ -1,4 +1,4 @@

-
+
\ No newline at end of file