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