diff options
171 files changed, 74021 insertions, 110 deletions
diff --git a/.bzrignore b/.bzrignore index 3140ce31c94..2d610a792e7 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1145,4 +1145,6 @@ sql/db.opt typescript storage/perfschema/gen_pfs_lex_token storage/perfschema/pfs_lex_token.h +*.bak +*.OLD mysql-test/collections/default.release.done diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index e79d97e045e..4e8507a5c4a 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -37,7 +37,7 @@ sub add_opt_values { # add auto-options $config->insert('OPT', 'port' => sub { fix_port($self, $config) }); - $config->insert('mysqld', "loose-skip-$_" => undef) for (@::optional_plugins); + $config->insert('mysqld', "loose-skip-plugin-$_" => undef) for (@::optional_plugins); } my @pre_rules= diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 7d263878bac..a6780b5038c 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3484,7 +3484,7 @@ sub mysql_install_db { mtr_add_arg($args, "--basedir=%s", $install_basedir); mtr_add_arg($args, "--datadir=%s", $install_datadir); mtr_add_arg($args, "--default-storage-engine=myisam"); - mtr_add_arg($args, "--skip-$_") for @optional_plugins; + mtr_add_arg($args, "--skip-plugin-$_") for @optional_plugins; # starting from 10.0 bootstrap scripts require InnoDB mtr_add_arg($args, "--loose-innodb"); mtr_add_arg($args, "--disable-sync-frm"); diff --git a/sql/handler.h b/sql/handler.h index 397d273c60d..a5fb0d8fd9d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2049,7 +2049,7 @@ public: cached_table_flags= table_flags(); } /* ha_ methods: pubilc wrappers for private virtual API */ - + int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked); int ha_index_init(uint idx, bool sorted) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0b2daade40e..ea0a19fc5f3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5091,100 +5091,6 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table, } -#ifndef NO_EMBEDDED_ACCESS_CHECKS -/** - Check grants for commands which work only with one table. - - @param thd Thread handler - @param privilege requested privilege - @param all_tables global table list of query - @param no_errors FALSE/TRUE - report/don't report error to - the client (using my_error() call). - - @retval - 0 OK - @retval - 1 access denied, error is sent to client -*/ - -bool check_single_table_access(THD *thd, ulong privilege, - TABLE_LIST *all_tables, bool no_errors) -{ - Security_context * backup_ctx= thd->security_ctx; - - /* we need to switch to the saved context (if any) */ - if (all_tables->security_ctx) - thd->security_ctx= all_tables->security_ctx; - - const char *db_name; - if ((all_tables->view || all_tables->field_translation) && - !all_tables->schema_table) - db_name= all_tables->view_db.str; - else - db_name= all_tables->db; - - if (check_access(thd, privilege, db_name, - &all_tables->grant.privilege, - &all_tables->grant.m_internal, - 0, no_errors)) - goto deny; - - /* Show only 1 table for check_grant */ - if (!(all_tables->belong_to_view && - (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && - check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) - goto deny; - - thd->security_ctx= backup_ctx; - return 0; - -deny: - thd->security_ctx= backup_ctx; - return 1; -} - -/** - Check grants for commands which work only with one table and all other - tables belonging to subselects or implicitly opened tables. - - @param thd Thread handler - @param privilege requested privilege - @param all_tables global table list of query - - @retval - 0 OK - @retval - 1 access denied, error is sent to client -*/ - -bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) -{ - if (check_single_table_access (thd,privilege,all_tables, FALSE)) - return 1; - - /* Check rights on tables of subselects and implictly opened tables */ - TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0; - if ((subselects_tables= all_tables->next_global)) - { - /* - Access rights asked for the first table of a view should be the same - as for the view - */ - if (view && subselects_tables->belong_to_view == view) - { - if (check_single_table_access (thd, privilege, subselects_tables, FALSE)) - return 1; - subselects_tables= subselects_tables->next_global; - } - if (subselects_tables && - (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE, - UINT_MAX, FALSE))) - return 1; - } - return 0; -} - - /** @brief Compare requested privileges with the privileges acquired from the User- and Db-tables. @@ -5217,6 +5123,11 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, GRANT_INTERNAL_INFO *grant_internal_info, bool dont_check_global_grants, bool no_errors) { +#ifdef NO_EMBEDDED_ACCESS_CHECKS + if (save_priv) + *save_priv= GLOBAL_ACLS; + return false; +#else Security_context *sctx= thd->security_ctx; ulong db_access; @@ -5399,6 +5310,101 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, "unknown"))); } DBUG_RETURN(TRUE); +#endif // NO_EMBEDDED_ACCESS_CHECKS +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/** + Check grants for commands which work only with one table. + + @param thd Thread handler + @param privilege requested privilege + @param all_tables global table list of query + @param no_errors FALSE/TRUE - report/don't report error to + the client (using my_error() call). + + @retval + 0 OK + @retval + 1 access denied, error is sent to client +*/ + +bool check_single_table_access(THD *thd, ulong privilege, + TABLE_LIST *all_tables, bool no_errors) +{ + Security_context * backup_ctx= thd->security_ctx; + + /* we need to switch to the saved context (if any) */ + if (all_tables->security_ctx) + thd->security_ctx= all_tables->security_ctx; + + const char *db_name; + if ((all_tables->view || all_tables->field_translation) && + !all_tables->schema_table) + db_name= all_tables->view_db.str; + else + db_name= all_tables->db; + + if (check_access(thd, privilege, db_name, + &all_tables->grant.privilege, + &all_tables->grant.m_internal, + 0, no_errors)) + goto deny; + + /* Show only 1 table for check_grant */ + if (!(all_tables->belong_to_view && + (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && + check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) + goto deny; + + thd->security_ctx= backup_ctx; + return 0; + +deny: + thd->security_ctx= backup_ctx; + return 1; +} + +/** + Check grants for commands which work only with one table and all other + tables belonging to subselects or implicitly opened tables. + + @param thd Thread handler + @param privilege requested privilege + @param all_tables global table list of query + + @retval + 0 OK + @retval + 1 access denied, error is sent to client +*/ + +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) +{ + if (check_single_table_access (thd,privilege,all_tables, FALSE)) + return 1; + + /* Check rights on tables of subselects and implictly opened tables */ + TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0; + if ((subselects_tables= all_tables->next_global)) + { + /* + Access rights asked for the first table of a view should be the same + as for the view + */ + if (view && subselects_tables->belong_to_view == view) + { + if (check_single_table_access (thd, privilege, subselects_tables, FALSE)) + return 1; + subselects_tables= subselects_tables->next_global; + } + if (subselects_tables && + (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE, + UINT_MAX, FALSE))) + return 1; + } + return 0; } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 25c41cc624c..346a3c8899b 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -148,6 +148,15 @@ inline bool check_identifier_name(LEX_STRING *str) return check_identifier_name(str, NAME_CHAR_LEN, 0, ""); } + +/* + check_access() is needed for the connect engine. + It cannot be inlined - it must be exported. +*/ +bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, + GRANT_INTERNAL_INFO *grant_internal_info, + bool dont_check_global_grants, bool no_errors); + #ifndef NO_EMBEDDED_ACCESS_CHECKS bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); bool check_single_table_access(THD *thd, ulong privilege, @@ -156,9 +165,6 @@ bool check_routine_access(THD *thd,ulong want_access,char *db,char *name, bool is_proc, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); -bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, - GRANT_INTERNAL_INFO *grant_internal_info, - bool dont_check_global_grants, bool no_errors); bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, uint number, @@ -180,13 +186,6 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) inline bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc) { return false; } -inline bool check_access(THD *, ulong, const char *, ulong *save_priv, - GRANT_INTERNAL_INFO *, bool, bool) -{ - if (save_priv) - *save_priv= GLOBAL_ACLS; - return false; -} inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 675ee615bc1..8ad9f10bc24 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4091,8 +4091,7 @@ handler *mysql_create_frm_image(THD *thd, if (!alter_info->create_list.elements) { - my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS), - MYF(0)); + my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0)); DBUG_RETURN(NULL); } diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt new file mode 100644 index 00000000000..5541efca880 --- /dev/null +++ b/storage/connect/CMakeLists.txt @@ -0,0 +1,278 @@ +# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(CONNECT_PLUGIN_STATIC "connect") +SET(CONNECT_PLUGIN_DYNAMIC "connect") + +SET(CONNECT_SOURCES +ha_connect.cc connect.cc user_connect.cc mycat.cc +fmdlex.c osutil.c plugutil.c rcmsg.c +csort.cpp maputil.cpp plgdbutl.cpp +colblk.cpp reldef.cpp tabcol.cpp table.cpp +filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamvct.cpp +tabdos.cpp tabfix.cpp tabfmt.cpp tabmul.cpp tabsys.cpp tabvct.cpp +valblk.cpp value.cpp xindex.cpp xobject.cpp +filamzip.cpp tabtbl.cpp myutil.cpp +block.h catalog.h checklvl.h colblk.h connect.h csort.h engmsg.h +filamap.h filamdbf.h filamfix.h filamtxt.h filamvct.h filamzip.h +global.h ha_connect.h maputil.h msgid.h mycat.h myutil.h os.h +osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h resource.h tabcol.h +tabdos.h tabfix.h tabfmt.h tabmul.h tabsys.h tabtbl.h tabvct.h +user_connect.h valblk.h value.h xindex.h xobject.h xtable.h) + +# +# Definitions that are shared for all OSes +# +add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS ) +add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT ) + + +# +# OS specific C flags, definitions and source files. +# +IF(UNIX) + if(WITH_WARNINGS) + add_definitions(-Wall -Wfatal-errors -Wextra -Wmissing-declarations) + add_definitions(-Wno-non-virtual-dtor) + message(STATUS "CONNECT: GCC: All warnings enabled") + else() + add_definitions(-Wall -Wfatal-errors -Wmissing-declarations) + add_definitions(-Wno-write-strings) + add_definitions(-Wno-unused-variable) + add_definitions(-Wno-unused-but-set-variable) + add_definitions(-Wno-unused-value) + add_definitions(-Wno-unused-function) + add_definitions(-Wno-parentheses) + add_definitions(-Wno-missing-declarations) + add_definitions(-Wno-int-to-pointer-cast) + add_definitions(-Wno-narrowing) + add_definitions(-Wno-non-virtual-dtor) + +# This switch is for pure C only: +# add_definitions(-Wno-implicit-function-declaration) +# These switches are for C++ only +# add_definitions(-Wno-reorder) +# add_definitions(-Wno-delete-non-virtual-dtor) + + message(STATUS "CONNECT: GCC: Some warnings disabled") + endif(WITH_WARNINGS) + + add_definitions( -DUNIX -DLINUX -DUBUNTU ) + + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -fexceptions -fPIC ") + get_property(inc_dirs DIRECTORY PROPERTY INCLUDE_DIRECTORIES) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} inihandl.c) + SET(IPHLPAPI_LIBRARY "") +ELSE(NOT UNIX) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} + tabwmi.cpp tabwmi.h tabmac.cpp tabmac.h macutil.cpp macutil.h) + # Add exception handling to the CONNECT project) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + SET(IPHLPAPI_LIBRARY iphlpapi.lib) +ENDIF(UNIX) + + +# +# XML +# + +OPTION(CONNECT_WITH_LIBXML2 "Compile CONNECT storage engine with LIBXML2 support" ON) + +IF(CONNECT_WITH_LIBXML2) + IF(WIN32) + # + # NOTE: when switching to static linking of libxml2 + # make sure to define LIBXML_STATIC. + # + # Adding some typical places to search in + SET(PC_LIBXML_INCLUDE_DIRS + C:/libxml2/include + C:/libxml/include + D:/libxml/include) + SET(PC_LIBXML_LIBRARY_DIRS + C:/libxml2/lib + C:/libxml/lib + D:/libxml/lib) + ENDIF(WIN32) + FIND_PACKAGE(LibXml2) + IF (LIBXML2_FOUND) + INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + SET(XML_LIBRARY ${LIBXML2_LIBRARIES}) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} libdoc.cpp libdoc.h) + add_definitions(-DLIBXML2_SUPPORT) + ENDIF(LIBXML2_FOUND) +ENDIF(CONNECT_WITH_LIBXML2) + + +IF(WIN32) + # /MP option of the Microsoft compiler does not work well with COM #import + string(REPLACE "/MP" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "/MP" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + + OPTION(CONNECT_WITH_MSXML "Compile CONNECT storage engine with MSXML support" ON) + IF(CONNECT_WITH_MSXML) + find_library(MSXML_LIBRARY + NAMES msxml6 msxml4 msxml3 msxml2 + PATHS + "C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib" + "C:/Program Files/Microsoft SDKs/Windows/v6.0A/Lib" + "C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib" + DOC "Specify the MSXML? library here." + ) + IF(MSXML_LIBRARY MATCHES .*msxml6[.].*) + add_definitions(-DMSX6 -DDOMDOC_SUPPORT) + message(STATUS "MSXML library version: msxml6") + SET(MSXML_FOUND 1) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} domdoc.cpp domdoc.h) + ELSEIF(MSXML_LIBRARY MATCHES .*msxml4[.].*) + add_definitions(-DMSX4 -DDOMDOC_SUPPORT) + message("MSXML library version: msxml4") + SET(MSXML_FOUND 1) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} domdoc.cpp domdoc.h) + ELSEIF(MSXML_LIBRARY MATCHES .*msxml3[.].*) + message("MSXML library version: msxml3") + add_definitions(-DMSX3 -DDOMDOC_SUPPORT) + SET(MSXML_FOUND 1) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} domdoc.cpp domdoc.h) + ELSEIF(MSXML_LIBRARY MATCHES .*msxml2[.].*) + message("MSXML library version: msxml2") + add_definitions(-DMXS2 -DDOMDOC_SUPPORT) + SET(MSXML_FOUND 1) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} domdoc.cpp domdoc.h) + ELSE() + message(STATUS "msxml? library not found") + ENDIF() + ENDIF(CONNECT_WITH_MSXML) +ENDIF(WIN32) + +IF(LIBXML2_FOUND OR MSXML_FOUND) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} + tabxml.cpp tabxml.h plgxml.cpp plgxml.h) +ENDIF() + +# +# MySQL +# + +OPTION(CONNECT_WITH_MYSQL + "Compile CONNECT storage engine with remote MySQL connection support" + ON) + +IF(CONNECT_WITH_MYSQL) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} + myconn.cpp myconn.h tabmysql.cpp tabxml.h) + add_definitions(-DMYSQL_SUPPORT) + IF(NOT UNIX) + # + # TODO: remove this + # change to use "#include "../../include/mysql.h" in the sources. + INCLUDE_DIRECTORIES("../../include/mysql") + ENDIF(NOT UNIX) +ENDIF(CONNECT_WITH_MYSQL) + + +# +# ODBC +# + +OPTION(CONNECT_WITH_ODBC "Compile CONNECT storage engine with ODBC support" ON) + +IF(CONNECT_WITH_ODBC) + if(UNIX) + # Note, we currently detect unixODBC only on Linux. + # TODO: detect iODBC as well. Simply adding "iodbc" into NAMES in + # find_library does not work on machines with both unixODBC and iODBC + # installed, because it finds headers from unixODBC while libraries + # from iODBC. We could search for 'isql.h' instead of 'sql.h' so + # the library 'libodbc' gets compiled with 'isql.h' and + # the library 'libiodbc' gets compiled with 'sql'h. + # This will also need changes in the sources (e.g. #include <isql.h>). + + find_path(ODBC_INCLUDE_DIR sql.h + /usr/include + /usr/include/odbc + /usr/local/include + /usr/local/include/odbc + /usr/local/odbc/include + #"C:/Program Files/ODBC/include" + #"C:/Program Files/Microsoft SDKs/Windows/v7.0A/include" + #"C:/Program Files/Microsoft SDKs/Windows/v6.0a/include" + #"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/include" + DOC "Specify the directory containing sql.h." + ) + + find_library(ODBC_LIBRARY + NAMES odbc odbcinst odbc32 + PATHS + /usr/lib + /usr/lib/odbc + /usr/local/lib + /usr/local/lib/odbc + /usr/local/odbc/lib + #"C:/Program Files/ODBC/lib" + #"C:/ODBC/lib/debug" + #"C:/Program Files/Microsoft SDKs/Windows/v7.0A/Lib" + #"C:/Program Files/Microsoft SDKs/Windows/v6.0A/Lib" + #"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.0A/Lib" + DOC "Specify the ODBC driver manager library here." + ) + + IF(ODBC_INCLUDE_DIR AND ODBC_LIBRARY) + INCLUDE_DIRECTORIES(${ODBC_INCLUDE_DIR}) + add_definitions(-DODBC_SUPPORT) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} tabodbc.cpp odbconn.cpp) + ELSE() + SET(ODBC_LIBRARY "") + ENDIF() + ELSE(NOT UNIX) + add_definitions(-DODBC_SUPPORT) + SET(ODBC_LIBRARY odbc32.lib odbccp32.lib) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} + tabodbc.cpp tabodbc.h odbccat.h odbconn.cpp odbconn.h) + ENDIF(UNIX) +ENDIF(CONNECT_WITH_ODBC) + + +# +# Plugin definition +# + +MYSQL_ADD_PLUGIN(connect ${CONNECT_SOURCES} + STORAGE_ENGINE + MODULE_OUTPUT_NAME "ha_connect" + COMPONENT connect_engine + LINK_LIBRARIES ${ZLIB_LIBRARY} ${XML_LIBRARY} ${ICONV_LIBRARY} + ${ODBC_LIBRARY} ${IPHLPAPI_LIBRARY}) + + +# +# Packaging definitions +# + +IF (INSTALL_SYSCONFDIR) + INSTALL(FILES connect.cnf DESTINATION ${INSTALL_SYSCONFDIR}/my.cnf.d + COMPONENT connect_engine) +ENDIF(INSTALL_SYSCONFDIR) + +IF(RPM) + SET(CPACK_COMPONENT_CASSANDRASELIBRARIES_GROUP "connect_engine" PARENT_SCOPE) + SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} connect_engine PARENT_SCOPE) + SET(CPACK_RPM_connect_engine_PACKAGE_REQUIRES "MariaDB-server" PARENT_SCOPE) + + # workarounds for cmake issues #13248 and #12864: + SET(CPACK_RPM_connect_engine_USER_FILELIST ${ignored} "%config(noreplace) /etc/my.cnf.d/*" PARENT_SCOPE) + SET(CPACK_RPM_connect_engine_PACKAGE_PROVIDES "cmake_bug_13248" PARENT_SCOPE) + SET(CPACK_RPM_connect_engine_PACKAGE_OBSOLETES "cmake_bug_13248" PARENT_SCOPE) +ENDIF(RPM) diff --git a/storage/connect/block.h b/storage/connect/block.h new file mode 100644 index 00000000000..963cfa42f32 --- /dev/null +++ b/storage/connect/block.h @@ -0,0 +1,57 @@ +/**************** Block H Declares Source Code File (.H) ***************/ +/* Name: BLOCK.H Version 2.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998 */ +/* */ +/* This file contains the BLOCK pure virtual class definition. */ +/*---------------------------------------------------------------------*/ +/* Note: one of the main purpose of this base class is to take care */ +/* of the very specific way Plug handles memory allocation. */ +/* Instead of allocating small chunks of storage via new or malloc */ +/* Plug works in its private memory pool in which it does the sub- */ +/* allocation using the function PlugSubAlloc. These are never freed */ +/* separately but when a transaction is terminated, the entire pool */ +/* is set to empty, resulting in a very fast and efficient allocate */ +/* process, no garbage collection problem, and an automatic recovery */ +/* procedure (via LongJump) when the memory is exhausted. */ +/* For this to work new must be given two parameters, first the */ +/* global pointer of the Plug application, and an optional pointer to */ +/* the memory pool to use, defaulting to NULL meaning using the Plug */ +/* standard default memory pool, example: */ +/* tabp = new(g) XTAB("EMPLOYEE"); */ +/* allocates a XTAB class object in the standard Plug memory pool. */ +/***********************************************************************/ +#if !defined(BLOCK_DEFINED) +#define BLOCK_DEFINED + +#if defined(WIN32) && !defined(NOEX) +#define DllExport __declspec( dllexport ) +#else // !WIN32 +#define DllExport +#endif // !WIN32 + +/***********************************************************************/ +/* Definition of class BLOCK with its method function new. */ +/***********************************************************************/ +typedef class BLOCK *PBLOCK; + +class DllExport BLOCK { + public: + void * operator new(size_t size, PGLOBAL g, void *p = NULL) { +#ifdef DEBTRACE +if (debug != NULL) + htrc("New BLOCK: size=%d g=%p p=%p\n", size, g, p); +#endif + return (PlugSubAlloc(g, p, size)); + } // end of new + + virtual void Print(PGLOBAL, FILE *, uint) {} // Produce file desc + virtual void Print(PGLOBAL, char *, uint) {} // Produce string desc + +#if !defined(__BORLANDC__) + // Avoid warning C4291 by defining a matching dummy delete operator + void operator delete(void *, PGLOBAL, void *) {} +#endif + }; // end of class BLOCK + +#endif // !BLOCK_DEFINED diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h new file mode 100644 index 00000000000..4f0584ef7bc --- /dev/null +++ b/storage/connect/catalog.h @@ -0,0 +1,130 @@ +/*************** Catalog H Declares Source Code File (.H) **************/ +/* Name: CATALOG.H Version 3.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2000-2012 */ +/* */ +/* This file contains the CATALOG PlugDB classes definitions. */ +/***********************************************************************/ +#ifndef __CATALOG__H +#define __CATALOG__H + +#include "block.h" + +/***********************************************************************/ +/* Defines the length of a buffer to contain entire table section. */ +/***********************************************************************/ +#define PLG_MAX_PATH 144 /* Must be the same across systems */ +#define PLG_BUFF_LEN 100 /* Number of lines in binary file buffer */ + + +//typedef class INDEXDEF *PIXDEF; + +/***********************************************************************/ +/* Defines the structure used to enumerate tables or views. */ +/***********************************************************************/ +typedef struct _curtab { + PRELDEF CurTdb; + char *Curp; + char *Tabpat; + bool Ispat; + bool NoView; + int Nt; + char *Type[16]; + } CURTAB, *PCURTAB; + +/***********************************************************************/ +/* Defines the structure used to get column catalog info. */ +/***********************************************************************/ +typedef struct _colinfo { + char *Name; + int Type; + int Offset; + int Length; + int Key; + int Prec; + int Opt; + char *Remark; + char *Datefmt; + char *Fieldfmt; + ushort Flags; // Used by MariaDB CONNECT handlers + } COLINFO, *PCOLINFO; + +/***********************************************************************/ +/* CATALOG: base class for catalog classes. */ +/***********************************************************************/ +class DllExport CATALOG { + friend class RELDEF; + friend class TABDEF; + friend class DIRDEF; + friend class OEMDEF; + public: + CATALOG(void); // Constructor + virtual ~CATALOG() { } // Make -Wdelete-non-virtual-dtor happy + + // Implementation + void *GetDescp(void) {return Descp;} + PRELDEF GetTo_Desc(void) {return To_Desc;} +//PSZ GetDescFile(void) {return DescFile;} + int GetCblen(void) {return Cblen;} + bool GetDefHuge(void) {return DefHuge;} + void SetDefHuge(bool b) {DefHuge = b;} +//bool GetSepIndex(void) {return SepIndex;} +//void SetSepIndex(bool b) {SepIndex = b;} + char *GetCbuf(void) {return Cbuf;} + char *GetDataPath(void) {return (char*)DataPath;} + + // Methods + virtual void Reset(void) {} + virtual void SetDataPath(PGLOBAL g, const char *path) {} + virtual bool GetBoolCatInfo(PSZ what, bool bdef) {return bdef;} + virtual bool SetIntCatInfo(PSZ what, int ival) {return false;} + virtual int GetIntCatInfo(PSZ what, int idef) {return idef;} + virtual int GetSizeCatInfo(PSZ what, PSZ sdef) {return 0;} + virtual int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size) + {strncpy(buf, sdef, size); return size;} + virtual char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef) + {return sdef;} + virtual int GetColCatInfo(PGLOBAL g, PTABDEF defp) {return -1;} + virtual bool GetIndexInfo(PGLOBAL g, PTABDEF defp) {return true;} + virtual bool CheckName(PGLOBAL g, char *name) {return true;} + virtual bool ClearName(PGLOBAL g, PSZ name) {return true;} + virtual PRELDEF MakeOneTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} + virtual PRELDEF GetTableDescEx(PGLOBAL g, PTABLE tablep) {return NULL;} + virtual PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, LPCSTR type, + PRELDEF *prp = NULL) {return NULL;} + virtual PRELDEF GetFirstTable(PGLOBAL g) {return NULL;} + virtual PRELDEF GetNextTable(PGLOBAL g) {return NULL;} + virtual bool TestCond(PGLOBAL g, const char *name, const char *type) + {return true;} + virtual bool DropTable(PGLOBAL g, PSZ name, bool erase) {return true;} + virtual PTDB GetTable(PGLOBAL g, PTABLE tablep, + MODE mode = MODE_READ, LPCSTR type = NULL) + {return NULL;} + virtual void TableNames(PGLOBAL g, char *buffer, int maxbuf, int info[]) {} + virtual void ColumnNames(PGLOBAL g, char *tabname, char *buffer, + int maxbuf, int info[]) {} + virtual void ColumnDefs(PGLOBAL g, char *tabname, char *buffer, + int maxbuf, int info[]) {} + virtual void *DecodeValues(PGLOBAL g, char *tabname, char *colname, + char *buffer, int maxbuf, int info[]) {return NULL;} + virtual int ColumnType(PGLOBAL g, char *tabname, char *colname) {return 0;} + virtual void ClearDB(PGLOBAL g) {} + + protected: + virtual bool ClearSection(PGLOBAL g, const char *key, const char *section) {return true;} + virtual PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) {return NULL;} + + // Members + PRELDEF To_Desc; /* To chain of relation desc. */ + void *Descp; /* To DB description area */ +//AREADEF DescArea; /* Table desc. area size */ + char *Cbuf; /* Buffer used for col section */ + int Cblen; /* Length of suballoc. buffer */ + CURTAB Ctb; /* Used to enumerate tables */ + bool DefHuge; /* true: tables default to huge */ +//bool SepIndex; /* true: separate index files */ +//char DescFile[_MAX_PATH]; /* DB description filename */ + LPCSTR DataPath; /* Is the Path of DB data dir */ + }; // end of class CATALOG + +#endif // __CATALOG__H diff --git a/storage/connect/checklvl.h b/storage/connect/checklvl.h new file mode 100644 index 00000000000..5505534678d --- /dev/null +++ b/storage/connect/checklvl.h @@ -0,0 +1,42 @@ +/************** PlgDBSem H Declares Source Code File (.H) **************/ +/* Name: CHKLVL.H Version 1.1 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2009 */ +/* */ +/* This file contains the definition of the checking level constants. */ +/***********************************************************************/ + +#if !defined(_CHKLVL_DEFINED_) +#define _CHKLVL_DEFINED_ +/***********************************************************************/ +/* Following definitions are used to indicate the level of checking. */ +/***********************************************************************/ +enum CHKLVL {CHK_NO = 0x00, /* No checking */ + CHK_TYPE = 0x01, /* Check types for Insert/Update */ + CHK_UPDATE = 0x02, /* Two pass checking of Update */ + CHK_DELETE = 0x04, /* Indexed checking of Delete */ + CHK_JOIN = 0x08, /* Check types joining tables */ + CHK_OPT = 0x10, /* Automatic optimize on changes */ + CHK_MANY = 0x20, /* Check many-to-many joins */ + CHK_ALL = 0x3F, /* All of the above */ + CHK_STD = 0x1E, /* Standard level of checking */ + CHK_MAXRES = 0x40, /* Prevent Maxres recalculation */ + CHK_ONLY = 0x100}; /* Just check, no action (NIY) */ + +/***********************************************************************/ +/* Following definitions are used to indicate the execution mode. */ +/***********************************************************************/ +enum XMOD {XMOD_EXECUTE = 0, /* DOS execution mode */ + XMOD_PREPARE = 1, /* Prepare mode */ + XMOD_TEST = 2, /* Test mode */ + XMOD_CONVERT = 3}; /* HQL conversion mode */ + +/***********************************************************************/ +/* Following definitions indicate the use of a temporay file. */ +/***********************************************************************/ +enum USETEMP {TMP_AUTO = 0, /* Best choice */ + TMP_NO = 1, /* Never */ + TMP_YES = 2, /* Always */ + TMP_FORCE = 3}; /* Forced for MAP tables */ + +#endif // _CHKLVL_DEFINED_ diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp new file mode 100644 index 00000000000..cdbf6f4b42b --- /dev/null +++ b/storage/connect/colblk.cpp @@ -0,0 +1,382 @@ +/************* Colblk C++ Functions Source Code File (.CPP) ************/ +/* Name: COLBLK.CPP Version 2.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */ +/* */ +/* This file contains the COLBLK class functions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "tabcol.h" +#include "colblk.h" +#include "xindex.h" +#include "xtable.h" + +/***********************************************************************/ +/* COLBLK protected constructor. */ +/***********************************************************************/ +COLBLK::COLBLK(PCOLDEF cdp, PTDB tdbp, int i) + { + Next = NULL; + Index = i; +//Number = 0; + ColUse = 0; + + if ((Cdp = cdp)) { + Name = cdp->Name; + Format = cdp->F; + Opt = cdp->Opt; + Long = cdp->Long; + Buf_Type = cdp->Buf_Type; + ColUse |= cdp->Flags; // Used by CONNECT + Nullable = !!(cdp->Flags & U_NULLS); + } else { + Name = NULL; + memset(&Format, 0, sizeof(FORMAT)); + Opt = 0; + Long = 0; + Buf_Type = TYPE_ERROR; + Nullable = false; + } // endif cdp + + To_Tdb = tdbp; + Status = BUF_NO; +//Value = NULL; done in XOBJECT constructor + To_Kcol = NULL; + } // end of COLBLK constructor + +/***********************************************************************/ +/* COLBLK constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +COLBLK::COLBLK(PCOL col1, PTDB tdbp) + { + PCOL colp; + + // Copy the old column block to the new one + *this = *col1; + Next = NULL; +//To_Orig = col1; + To_Tdb = tdbp; + +#ifdef DEBTRACE + htrc(" copying COLBLK %s from %p to %p\n", Name, col1, this); +#endif + + if (tdbp) + // Attach the new column to the table block + if (!tdbp->GetColumns()) + tdbp->SetColumns(this); + else { + for (colp = tdbp->GetColumns(); colp->Next; colp = colp->Next) ; + + colp->Next = this; + } // endelse + + } // end of COLBLK copy constructor + +/***********************************************************************/ +/* Reset the column descriptor to non evaluated yet. */ +/***********************************************************************/ +void COLBLK::Reset(void) + { + Status &= ~BUF_READ; + } // end of Reset + +/***********************************************************************/ +/* Compare: compares itself to an (expression) object and returns */ +/* true if it is equivalent. */ +/***********************************************************************/ +bool COLBLK::Compare(PXOB xp) + { + return (this == xp); + } // end of Compare + +/***********************************************************************/ +/* SetFormat: function used to set SELECT output format. */ +/***********************************************************************/ +bool COLBLK::SetFormat(PGLOBAL g, FORMAT& fmt) + { + fmt = Format; + +#ifdef DEBTRACE + htrc("COLBLK: %p format=%c(%d,%d)\n", + this, *fmt.Type, fmt.Length, fmt.Prec); +#endif + + return false; + } // end of SetFormat + +/***********************************************************************/ +/* CheckColumn: a column descriptor is found, say it by returning 1. */ +/***********************************************************************/ +int COLBLK::CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &p, int &ag) + { + return 1; + } // end of CheckColumn + +/***********************************************************************/ +/* Eval: get the column value from the last read record or from a */ +/* matching Index column if there is one. */ +/***********************************************************************/ +bool COLBLK::Eval(PGLOBAL g) + { +#ifdef DEBTRACE + htrc("Col Eval: %s status=%.4X\n", Name, Status); +#endif + + if (!GetStatus(BUF_READ)) { +// if (To_Tdb->IsNull()) +// Value->Reset(); + if (To_Kcol) + To_Kcol->FillValue(Value); + else + ReadColumn(g); + + AddStatus(BUF_READ); + } // endif + + return false; + } // end of Eval + +/***********************************************************************/ +/* CheckSort: */ +/* Used to check that a table is involved in the sort list items. */ +/***********************************************************************/ +bool COLBLK::CheckSort(PTDB tdbp) + { + return (tdbp == To_Tdb); + } // end of CheckSort + +/***********************************************************************/ +/* MarkCol: see PlugMarkCol for column references to mark. */ +/***********************************************************************/ +void COLBLK::MarkCol(ushort bits) + { + ColUse |= bits; + +#ifdef DEBTRACE + htrc(" column R%d.%s marked as %04X\n", + To_Tdb->GetTdb_No(), Name, ColUse); +#endif + } // end of MarkCol + +/***********************************************************************/ +/* InitValue: prepare a column block for read operation. */ +/* Now we use Format.Length for the len parameter to avoid strings */ +/* to be truncated when converting from string to coded string. */ +/* Added in version 1.5 is the arguments GetPrecision() and Domain */ +/* in calling AllocateValue. Domain is used for TYPE_TOKEN only, */ +/* but why was GetPrecision() not specified ? To be checked. */ +/***********************************************************************/ +bool COLBLK::InitValue(PGLOBAL g) + { + if (Value) + return false; // Already done + + // Allocate a Value object + if (!(Value = AllocateValue(g, Buf_Type, Format.Length, + GetPrecision(), GetDomain(), + (To_Tdb) ? To_Tdb->GetCat() : NULL))) + return true; + + Status = BUF_READY; + Value->SetNullable(Nullable); + +#ifdef DEBTRACE + htrc(" colp=%p type=%d value=%p coluse=%.4X status=%.4X\n", + this, Buf_Type, Value, ColUse, Status); +#endif + + return false; + } // end of InitValue + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool COLBLK::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + sprintf(g->Message, MSG(UNDEFINED_AM), "SetBuffer"); + return true; + } // end of SetBuffer + +/***********************************************************************/ +/* GetLength: returns an evaluation of the column string length. */ +/***********************************************************************/ +int COLBLK::GetLengthEx(void) + { + return Long; + } // end of GetLengthEx + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the last line */ +/* read from the corresponding table, extract from it the field */ +/* corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void COLBLK::ReadColumn(PGLOBAL g) + { + sprintf(g->Message, MSG(UNDEFINED_AM), "ReadColumn"); + longjmp(g->jumper[g->jump_level], TYPE_COLBLK); + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void COLBLK::WriteColumn(PGLOBAL g) + { + sprintf(g->Message, MSG(UNDEFINED_AM), "WriteColumn"); + longjmp(g->jumper[g->jump_level], TYPE_COLBLK); + } // end of WriteColumn + +/***********************************************************************/ +/* Make file output of a column descriptor block. */ +/***********************************************************************/ +void COLBLK::Print(PGLOBAL g, FILE *f, uint n) + { + char m[64]; + int i; + PCOL colp; + + memset(m, ' ', n); // Make margin string + m[n] = '\0'; + + for (colp = To_Tdb->GetColumns(), i = 1; colp; colp = colp->Next, i++) + if (colp == this) + break; + + fprintf(f, "%sR%dC%d type=%d F=%.2s(%d,%d)", m, To_Tdb->GetTdb_No(), + i, GetAmType(), Format.Type, Format.Length, Format.Prec); + fprintf(f, + " coluse=%04X status=%04X buftyp=%d value=%p name=%s\n", + ColUse, Status, Buf_Type, Value, Name); + } // end of Print + +/***********************************************************************/ +/* Make string output of a column descriptor block. */ +/***********************************************************************/ +void COLBLK::Print(PGLOBAL g, char *ps, uint z) + { + sprintf(ps, "R%d.%s", To_Tdb->GetTdb_No(), Name); + } // end of Print + + +/***********************************************************************/ +/* SPCBLK constructor. */ +/***********************************************************************/ +SPCBLK::SPCBLK(PCOLUMN cp) + : COLBLK((PCOLDEF)NULL, cp->GetTo_Table()->GetTo_Tdb(), 0) + { + Name = (char*)cp->GetName(); + Long = 0; + Buf_Type = TYPE_ERROR; + } // end of SPCBLK constructor + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void SPCBLK::WriteColumn(PGLOBAL g) + { + sprintf(g->Message, MSG(SPCOL_READONLY), Name); + longjmp(g->jumper[g->jump_level], TYPE_COLBLK); + } // end of WriteColumn + +/***********************************************************************/ +/* RIDBLK constructor for the ROWID special column. */ +/***********************************************************************/ +RIDBLK::RIDBLK(PCOLUMN cp, bool rnm) : SPCBLK(cp) + { + Long = 10; + Buf_Type = TYPE_INT; + Rnm = rnm; + *Format.Type = 'N'; + Format.Length = 10; + } // end of RIDBLK constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to return the ordinal */ +/* number of the current row in the table (if Rnm is true) or in the */ +/* current file (if Rnm is false) the same except for multiple tables.*/ +/***********************************************************************/ +void RIDBLK::ReadColumn(PGLOBAL g) + { + Value->SetValue(To_Tdb->RowNumber(g, Rnm)); + } // end of ReadColumn + +/***********************************************************************/ +/* FIDBLK constructor for the FILEID special column. */ +/***********************************************************************/ +FIDBLK::FIDBLK(PCOLUMN cp) : SPCBLK(cp) + { +//Is_Key = 2; for when the MUL table indexed reading will be implemented. + Long = _MAX_PATH; + Buf_Type = TYPE_STRING; + *Format.Type = 'C'; + Format.Length = Long; +#if defined(WIN32) + Format.Prec = 1; // Case insensitive +#endif // WIN32 + Constant = (!((PTDBASE)To_Tdb)->GetDef()->GetMultiple() && + To_Tdb->GetAmType() != TYPE_AM_PLG && + To_Tdb->GetAmType() != TYPE_AM_PLM); + Fn = NULL; + } // end of FIDBLK constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to return the current */ +/* file ID of the table (can change for Multiple tables). */ +/***********************************************************************/ +void FIDBLK::ReadColumn(PGLOBAL g) + { + if (Fn != ((PTDBASE)To_Tdb)->GetFile(g)) { + char filename[_MAX_PATH]; + + Fn = ((PTDBASE)To_Tdb)->GetFile(g); + PlugSetPath(filename, Fn, ((PTDBASE)To_Tdb)->GetPath()); + Value->SetValue_psz(filename); + } // endif Fn + + } // end of ReadColumn + +/***********************************************************************/ +/* TIDBLK constructor for the TABID special column. */ +/***********************************************************************/ +TIDBLK::TIDBLK(PCOLUMN cp) : SPCBLK(cp) + { +//Is_Key = 2; for when the MUL table indexed reading will be implemented. + Long = 64; + Buf_Type = TYPE_STRING; + *Format.Type = 'C'; + Format.Length = Long; + Format.Prec = 1; // Case insensitive + Constant = (To_Tdb->GetAmType() != TYPE_AM_PLG && + To_Tdb->GetAmType() != TYPE_AM_PLM); + Tname = NULL; + } // end of TIDBLK constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to return the table ID. */ +/***********************************************************************/ +void TIDBLK::ReadColumn(PGLOBAL g) + { + if (Tname == NULL) { + Tname = (char*)To_Tdb->GetName(); + Value->SetValue_psz(Tname); + } // endif Tname + + } // end of ReadColumn + diff --git a/storage/connect/colblk.h b/storage/connect/colblk.h new file mode 100644 index 00000000000..cf6fb443308 --- /dev/null +++ b/storage/connect/colblk.h @@ -0,0 +1,183 @@ +/*************** Colblk H Declares Source Code File (.H) ***************/ +/* Name: COLBLK.H Version 1.6 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* This file contains the COLBLK and derived classes declares. */ +/***********************************************************************/ +#ifndef __COLBLK__H +#define __COLBLK__H + +/***********************************************************************/ +/* Include required application header files */ +/***********************************************************************/ +#include "xobject.h" +#include "reldef.h" + +/***********************************************************************/ +/* Class COLBLK: Base class for table column descriptors. */ +/***********************************************************************/ +class DllExport COLBLK : public XOBJECT { + friend class TDBPIVOT; + protected: + // Default constructors used by derived classes + COLBLK(PCOLDEF cdp = NULL, PTDB tdbp = NULL, int i = 0); + COLBLK(PCOL colp, PTDB tdbp = NULL); // Used in copy process + COLBLK(int n) {} // Used when changing a column class in TDBXML + + public: + // Implementation + virtual int GetType(void) {return TYPE_COLBLK;} + virtual int GetResultType(void) {return Buf_Type;} + virtual int GetPrecision(void) {return Format.Prec;} + virtual int GetLength(void) {return Long;} + virtual int GetLengthEx(void); + virtual int GetAmType() {return TYPE_AM_ERROR;} + virtual void SetOk(void) {Status |= BUF_EMPTY;} + virtual PTDB GetTo_Tdb(void) {return To_Tdb;} + PCOL GetNext(void) {return Next;} + PSZ GetName(void) {return Name;} + int GetIndex(void) {return Index;} + int GetOpt(void) {return Opt;} + ushort GetColUse(void) {return ColUse;} + ushort GetColUse(ushort u) {return (ColUse & u);} + ushort GetStatus(void) {return Status;} + ushort GetStatus(ushort u) {return (Status & u);} + void SetColUse(ushort u) {ColUse = u;} + void SetStatus(ushort u) {Status = u;} + void AddColUse(ushort u) {ColUse |= u;} + void AddStatus(ushort u) {Status |= u;} + void SetNext(PCOL cp) {Next = cp;} + void SetKcol(PXCOL kcp) {To_Kcol = kcp;} + PCOLDEF GetCdp(void) {return Cdp;} + PSZ GetDomain(void) {return (Cdp) ? Cdp->Decode : NULL;} + PSZ GetDesc(void) {return (Cdp) ? Cdp->Desc : NULL;} + PSZ GetFmt(void) {return (Cdp) ? Cdp->Fmt : NULL;} + bool IsNullable(void) {return Nullable;} + void SetNullable(bool b) {Nullable = b;} + + // Methods + virtual void Reset(void); + virtual bool Compare(PXOB xp); + virtual bool SetFormat(PGLOBAL, FORMAT&); + virtual int CheckColumn(PGLOBAL g, PSQL sqlp, PXOB &xp, int &ag); + virtual bool IsSpecial(void) {return false;} + virtual int CheckSpcCol(PTDB tdbp, int n) {return 2;} + virtual bool CheckSort(PTDB tdbp); + virtual void MarkCol(ushort bits); + virtual bool Eval(PGLOBAL g); + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void SetTo_Val(PVAL valp) {} + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + virtual void Print(PGLOBAL g, FILE *, uint); + virtual void Print(PGLOBAL g, char *, uint); + virtual bool VarSize(void) {return false;} + virtual bool IsColInside(PCOL colp) {return this == colp;} + bool InitValue(PGLOBAL g); + + protected: + // Members + PCOL Next; // Next column in table + PSZ Name; // Column name + PCOLDEF Cdp; // To column definition block + PTDB To_Tdb; // Points to Table Descriptor Block + PXCOL To_Kcol; // Points to Xindex matching column + bool Nullable; // True if nullable + int Index; // Column number in table + int Opt; // Cluster/sort information + int Buf_Type; // Data type + int Long; // Internal length in table + FORMAT Format; // Output format + ushort ColUse; // Column usage + ushort Status; // Column read status + }; // end of class COLBLK + +/***********************************************************************/ +/* Class SPCBLK: Base class for special column descriptors. */ +/***********************************************************************/ +class SPCBLK : public COLBLK { + public: + // Constructor + SPCBLK(PCOLUMN cp); + + // Implementation + virtual int GetAmType(void) = 0; + virtual bool GetRnm(void) {return false;} + + // Methods + virtual bool IsSpecial(void) {return true;} + virtual void ReadColumn(PGLOBAL g) = 0; + virtual void WriteColumn(PGLOBAL g); + + protected: + // Default constructor not to be used + SPCBLK(void) : COLBLK(1) {} + }; // end of class SPCBLK + +/***********************************************************************/ +/* Class RIDBLK: ROWID special column descriptor. */ +/***********************************************************************/ +class RIDBLK : public SPCBLK { + public: + // Constructor + RIDBLK(PCOLUMN cp, bool rnm); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_ROWID;} + virtual bool GetRnm(void) {return Rnm;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + protected: + bool Rnm; // False for RowID, True for RowNum + }; // end of class RIDBLK + +/***********************************************************************/ +/* Class FIDBLK: FILEID special column descriptor. */ +/***********************************************************************/ +class FIDBLK : public SPCBLK { + public: + // Constructor + FIDBLK(PCOLUMN cp); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_FILID;} + + // Methods + virtual void Reset(void) {} // This is a pseudo constant column + virtual int CheckSpcCol(PTDB tdbp, int n) + {return (n == 2 && tdbp == To_Tdb) ? 1 : 2;} + virtual void ReadColumn(PGLOBAL g); + + protected: + PSZ Fn; // The current To_File of the table + }; // end of class FIDBLK + +/***********************************************************************/ +/* Class TIDBLK: TABID special column descriptor. */ +/***********************************************************************/ +class TIDBLK : public SPCBLK { + public: + // Constructor + TIDBLK(PCOLUMN cp); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_TABID;} + + // Methods + virtual void Reset(void) {} // This is a pseudo constant column + virtual int CheckSpcCol(PTDB tdbp, int n) + {return (n == 3 && tdbp == To_Tdb) ? 1 : 2;} + virtual void ReadColumn(PGLOBAL g); + + protected: + // Default constructor not to be used + TIDBLK(void) {} + + // Members + PSZ Tname; // The current table name + }; // end of class TIDBLK + +#endif // __COLBLK__H diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc new file mode 100644 index 00000000000..54816f80e5c --- /dev/null +++ b/storage/connect/connect.cc @@ -0,0 +1,857 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/***********************************************************************/ +/* Author Olivier BERTRAND bertrandop@gmail.com 2004-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the CONNECT general purpose semantic routines. */ +/***********************************************************************/ +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +/***********************************************************************/ +/* Include application header files */ +/* */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/***********************************************************************/ +#define MYSQL_SERVER 1 +#define DONT_DEFINE_VOID +#include "handler.h" +#undef OFFSET + +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "connect.h" +#include "tabcol.h" +#include "catalog.h" +#include "ha_connect.h" +#include "mycat.h" + +#define my_strupr(p) my_caseup_str(default_charset_info, (p)); +#define my_strlwr(p) my_casedn_str(default_charset_info, (p)); +#define my_stricmp(a, b) my_strcasecmp(default_charset_info, (a), (b)) + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +extern int xtrace; + +/***********************************************************************/ +/* Routines called internally by semantic routines. */ +/***********************************************************************/ +void CntEndDB(PGLOBAL); +RCODE EvalColumns(PGLOBAL g, PTDB tdbp); + +/***********************************************************************/ +/* MySQL routines called externally by semantic routines. */ +/***********************************************************************/ +int rename_file_ext(const char *from, const char *to,const char *ext); + +/***********************************************************************/ +/* CntExit: CONNECT termination routine. */ +/***********************************************************************/ +PGLOBAL CntExit(PGLOBAL g) + { + if (g) { + PDBUSER dup= PlgGetUser(g); + + CntEndDB(g); + free(dup); + + if (g->Activityp) + delete g->Activityp; + + PlugExit(g); + g= NULL; + } // endif g + + return g; + } // end of CntExit + +/***********************************************************************/ +/* CntEndDB: DB termination semantic routine. */ +/***********************************************************************/ +void CntEndDB(PGLOBAL g) + { + PDBUSER dbuserp= PlgGetUser(g); + + if (dbuserp) { + if (dbuserp->Catalog) { + delete dbuserp->Catalog; + dbuserp->Catalog= NULL; + } // endif Catalog + + *dbuserp->Name= '\0'; +// *dbuserp->Work= '\0'; + } // endif dbuserp + + } // end of CntEndDB + +/***********************************************************************/ +/* CntCheckDB: Initialize a DB application session. */ +/* Note: because MySQL does not call a storage handler when a user */ +/* executes a use db command, a check must be done before an SQL */ +/* command is executed to check whether we are still working on the */ +/* current database, and if not to load the newly used database. */ +/***********************************************************************/ +bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname) + { + bool rc= false; + PDBUSER dbuserp= PlgGetUser(g); + + if (xtrace) { + printf("CntCheckDB: dbuserp=%p\n", dbuserp); + } // endif xtrace + + if (!dbuserp || !handler) + return true; + + if (xtrace) + printf("cat=%p oldhandler=%p newhandler=%p\n", dbuserp->Catalog, + (dbuserp->Catalog) ? ((MYCAT*)dbuserp->Catalog)->GetHandler() : NULL, + handler); + + if (dbuserp->Catalog) { +// ((MYCAT *)dbuserp->Catalog)->SetHandler(handler); done later + ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname); + return false; // Nothing else to do + } // endif Catalog + + // Copy new database name in dbuser block + strncpy(dbuserp->Name, "???", sizeof(dbuserp->Name) - 1); + + dbuserp->Vtdbno= 0; // Init of TDB numbers + + /*********************************************************************/ + /* Now allocate and initialize the Database Catalog. */ + /*********************************************************************/ + dbuserp->Step= MSG(READY); + + if (!(dbuserp->Catalog= new MYCAT(handler))) + return true; + + ((MYCAT *)dbuserp->Catalog)->SetDataPath(g, pathname); + dbuserp->UseTemp= TMP_YES; // Must use temporary file + + /*********************************************************************/ + /* All is correct. */ + /*********************************************************************/ + sprintf(g->Message, MSG(DATABASE_LOADED), "???"); + + if (xtrace) + printf("msg=%s\n", g->Message); + + return rc; + } // end of CntCheckDB + +/***********************************************************************/ +/* CntInfo: Get table info. */ +/* Returns valid: true if this is a table info. */ +/***********************************************************************/ +bool CntInfo(PGLOBAL g, PTDB tp, PXF info) + { + bool b; + PTDBDOS tdbp= (PTDBDOS)tp; + + if (tdbp) { + b= tdbp->GetFtype() != RECFM_NAF; + info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0; + info->records= (unsigned)tdbp->GetMaxSize(g); +// info->mean_rec_length= tdbp->GetLrecl(); + info->mean_rec_length= 0; + info->data_file_name= (b) ? tdbp->GetFile(g) : NULL; + return true; + } else { + info->data_file_length= 0; + info->records= 0; + info->mean_rec_length= 0; + info->data_file_name= NULL; + return false; + } // endif tdbp + + } // end of CntInfo + +/***********************************************************************/ +/* GetTDB: Get the table description block of a CONNECT table. */ +/***********************************************************************/ +PTDB CntGetTDB(PGLOBAL g, LPCSTR name, MODE mode, PHC h) + { + int rc; + PTDB tdbp; + PTABLE tabp; + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; // Safe over longjmp + + if (xtrace) + printf("CntGetTDB: name=%s mode=%d cat=%p\n", name, mode, cat); + + if (!cat) + return NULL; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + tdbp= NULL; + goto err; + } // endif rc + + // Get table object from the catalog + tabp= new(g) XTAB(name); + + if (xtrace) + printf("CntGetTDB: tabp=%p\n", tabp); + + // Perhaps this should be made thread safe + ((MYCAT*)cat)->SetHandler(h); + + if (!(tdbp= cat->GetTable(g, tabp, mode))) + printf("CntGetTDB: %s\n", g->Message); + + err: + if (xtrace) + printf("Returning tdbp=%p mode=%d\n", tdbp, mode); + + g->jump_level--; + return tdbp; + } // end of CntGetTDB + +/***********************************************************************/ +/* OPENTAB: Open a Table. */ +/***********************************************************************/ +bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2, + bool del, PHC h) + { + char *p; + int i, n; + PCOL colp; + PCOLUMN cp; + PDBUSER dup= PlgGetUser(g); + + if (xtrace) + printf("CntOpenTable: tdbp=%p mode=%d\n", tdbp, mode); + + if (!tdbp) { + strcpy(g->Message, "Null tdbp"); + printf("CntOpenTable: %s\n", g->Message); + return true; + } // endif tdbp + + if (!c1) + // Allocate all column blocks for that table + tdbp->ColDB(g, NULL, 0); + else for (p= c1; *p; p+= n) { + // Allocate only used column blocks + if (xtrace) + printf("Allocating column %s\n", p); + + if (*p == '*') { + // This is a special column + cp= new(g) COLUMN(p + 1); + cp->SetTo_Table(tdbp->GetTable()); + colp= ((PTDBASE)tdbp)->InsertSpcBlk(g, cp); + } else + colp= tdbp->ColDB(g, p, 0); + + if (!colp) { + sprintf(g->Message, "Column %s not found in %s", p, tdbp->GetName()); + return true; + } // endif colp + + n= strlen(p) + 1; + } // endfor p + + for (i= 0, colp= tdbp->GetColumns(); colp; i++, colp= colp->GetNext()) { + if (colp->InitValue(g)) + return true; + + if (mode == MODE_INSERT) + // Allow type conversion + if (colp->SetBuffer(g, colp->GetValue(), true, false)) + return true; + + colp->AddColUse(U_P); // For PLG tables + } // endfor colp + + /*********************************************************************/ + /* In Update mode, the updated column blocks must be distinct from */ + /* the read column blocks. So make a copy of the TDB and allocate */ + /* its column blocks in mode write (required by XML tables). */ + /*********************************************************************/ + if (mode == MODE_UPDATE) { + PTDBASE utp; + + if (!(utp= (PTDBASE)tdbp->Duplicate(g))) { + sprintf(g->Message, MSG(INV_UPDT_TABLE), tdbp->GetName()); + return true; + } // endif tp + + if (!c2) + // Allocate all column blocks for that table + utp->ColDB(g, NULL, 0); + else for (p= c2; *p; p+= n) { + // Allocate only used column blocks + colp= utp->ColDB(g, p, 0); + n= strlen(p) + 1; + } // endfor p + + for (i= 0, colp= utp->GetColumns(); colp; i++, colp= colp->GetNext()) { + if (colp->InitValue(g)) + return true; + + if (colp->SetBuffer(g, colp->GetValue(), true, false)) + return true; + + } // endfor colp + + // Attach the updated columns list to the main table + ((PTDBASE)tdbp)->SetSetCols(utp->GetColumns()); + } else if (tdbp && mode == MODE_INSERT) + ((PTDBASE)tdbp)->SetSetCols(tdbp->GetColumns()); + + // Now do open the physical table + if (xtrace) + printf("Opening table %s in mode %d tdbp=%p\n", + tdbp->GetName(), mode, tdbp); + + tdbp->SetMode(mode); + + if (del && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) { + // To avoid erasing the table when doing a partial delete + // make a fake Next + PDOSDEF ddp= new(g) DOSDEF; + PTDB tp= new(g) TDBDOS(ddp, NULL); + tdbp->SetNext(tp); + dup->Check &= ~CHK_DELETE; + } // endif del + + + if (xtrace) + printf("About to open the table: tdbp=%p\n", tdbp); + + if (mode != MODE_ANY) { + if (tdbp->OpenDB(g)) { + printf("%s\n", g->Message); + return true; + } else + tdbp->SetNext(NULL); + + } // endif mode + + return false; + } // end of CntOpenTable + +/***********************************************************************/ +/* Rewind a table by reopening it. */ +/***********************************************************************/ +bool CntRewindTable(PGLOBAL g, PTDB tdbp) +{ + if (!tdbp) + return true; + + tdbp->OpenDB(g); + return false; +} // end of CntRewindTable + +/***********************************************************************/ +/* Evaluate all columns after a record is read. */ +/***********************************************************************/ +RCODE EvalColumns(PGLOBAL g, PTDB tdbp) + { + RCODE rc= RC_OK; + PCOL colp; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + if (xtrace) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + printf("EvalColumns: %s\n", g->Message); + } // endif + + return RC_FX; + } // endif jump_level + + if (setjmp(g->jumper[++g->jump_level]) != 0) { + if (xtrace) + printf("Error reading columns: %s\n", g->Message); + + rc= RC_FX; + goto err; + } // endif rc + + for (colp= tdbp->GetColumns(); rc == RC_OK && colp; + colp= colp->GetNext()) { + colp->Reset(); + + // Virtual columns are computed by MariaDB + if (!colp->GetColUse(U_VIRTUAL)) + if (colp->Eval(g)) + rc= RC_FX; + + } // endfor colp + + err: + g->jump_level--; + return rc; + } // end of EvalColumns + +/***********************************************************************/ +/* ReadNext: Read next record sequentially. */ +/***********************************************************************/ +RCODE CntReadNext(PGLOBAL g, PTDB tdbp) + { + RCODE rc; + + if (!tdbp) + return RC_FX; + else if (((PTDBASE)tdbp)->GetKindex()) { + // Reading sequencially an indexed table. This happens after the + // handler function records_in_range was called and MySQL decides + // to quit using the index (!!!) Drop the index. + for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext()) + colp->SetKcol(NULL); + + ((PTDBASE)tdbp)->SetKindex(NULL); + } // endif index + + while ((rc= (RCODE)tdbp->ReadDB(g)) == RC_NF) ; + + return (rc != RC_OK) ? rc : EvalColumns(g, tdbp); + } // end of CntReadNext + +/***********************************************************************/ +/* WriteRow: Insert a new row into a table. */ +/***********************************************************************/ +RCODE CntWriteRow(PGLOBAL g, PTDB tdbp) + { + RCODE rc; + PCOL colp; + PTDBASE tp= (PTDBASE)tdbp; + + if (!tdbp) + return RC_FX; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return RC_FX; + } // endif jump_level + + if (setjmp(g->jumper[++g->jump_level]) != 0) { + printf("%s\n", g->Message); + rc= RC_FX; + goto err; + } // endif rc + + // Store column values in table write buffer(s) + for (colp= tp->GetSetCols(); colp; colp= colp->GetNext()) + if (!colp->GetColUse(U_VIRTUAL)) + colp->WriteColumn(g); + +// if (tdbp->GetMode() == MODE_INSERT) +// tbxp->SetModified(true); + + // Return result code from write operation + rc= (RCODE)tdbp->WriteDB(g); + + err: + g->jump_level--; + return rc; + } // end of CntWriteRow + +/***********************************************************************/ +/* UpdateRow: Update a row into a table. */ +/***********************************************************************/ +RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp) + { + if (!tdbp || tdbp->GetMode() != MODE_UPDATE) + return RC_FX; + + // Return result code from write operation + return CntWriteRow(g, tdbp); + } // end of CntUpdateRow + +/***********************************************************************/ +/* DeleteRow: Delete a row from a table. */ +/***********************************************************************/ +RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all) + { + RCODE rc; + + if (!tdbp || tdbp->GetMode() != MODE_DELETE) + return RC_FX; +// else +// ((PTDBDOX)tdbp)->SetModified(true); + + if (((PTDBASE)tdbp)->GetDef()->Indexable() && all) + ((PTDBDOS)tdbp)->Cardinal= 0; + + // Return result code from delete operation + // Note: if all, this call will be done when closing the table + rc= (RCODE)tdbp->DeleteDB(g, (all) ? RC_FX : RC_OK); + return rc; + } // end of CntDeleteRow + +/***********************************************************************/ +/* CLOSETAB: Close a table. */ +/***********************************************************************/ +int CntCloseTable(PGLOBAL g, PTDB tdbp) + { + int rc= RC_OK; + TDBDOX *tbxp= NULL; + + if (!tdbp) + return rc; // Already done + + if (xtrace) + printf("CntCloseTable: tdbp=%p mode=%d\n", tdbp, tdbp->GetMode()); + + /*********************************************************************/ + /* This will close the table file(s) and also finalize write */ + /* operations such as Insert, Update, or Delete. */ + /*********************************************************************/ + if (tdbp->GetMode() == MODE_DELETE) + rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + rc= RC_FX; + goto err; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + goto err; + } // endif + + tdbp->CloseDB(g); + + g->jump_level--; + + if (xtrace > 1) + printf("Table %s closed\n", tdbp->GetName()); + +//if (!((PTDBDOX)tdbp)->GetModified()) +// return 0; + + if (tdbp->GetMode() == MODE_READ || tdbp->GetMode() == MODE_ANY) + return 0; + + if (xtrace > 1) + printf("About to reset opt\n"); + + // Make all the eventual indexes + tbxp= (TDBDOX*)tdbp; + tbxp->SetKindex(NULL); + tbxp->To_Key_Col= NULL; + rc= tbxp->ResetTableOpt(g, ((PTDBASE)tdbp)->GetDef()->Indexable()); + + err: + if (xtrace > 1) + printf("Done rc=%d\n", rc); + + return (rc == RC_OK || rc == RC_INFO) ? 0 : rc; + } // end of CntCloseTable + +/***********************************************************************/ +/* Load and initialize the use of an index. */ +/* This is the condition(s) for doing indexing. */ +/* Note: FIX table are not reset here to Nrec= 1. */ +/***********************************************************************/ +int CntIndexInit(PGLOBAL g, PTDB ptdb, int id) + { + int k; + PCOL colp; + PVAL valp; + PKXBASE xp; + PXLOAD pxp; + PIXDEF xdp; + XKPDEF *kdp; + PTDBDOX tdbp; + PCOLDEF cdp; + DOXDEF *dfp; + + if (!ptdb) + return -1; + else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) { + sprintf(g->Message, "Table %s is not indexable", ptdb->GetName()); + return 0; + } else + tdbp= (PTDBDOX)ptdb; + + dfp= (DOXDEF*)tdbp->To_Def; + +//if (!(k= colp->GetKey())) +// if (colp->GetOpt() >= 2) { +// strcpy(g->Message, "Not a valid indexed column"); +// return -1; +// } else + // This is a pseudo indexed sorted block optimized column +// return 0; + + if (tdbp->To_Kindex) + if (((XXBASE*)tdbp->To_Kindex)->GetID() == id) { + tdbp->To_Kindex->Reset(); // Same index + return (tdbp->To_Kindex->IsMul()) ? 2 : 1; + } else { + tdbp->To_Kindex->Close(); + tdbp->To_Kindex= NULL; + } // endif colp + + for (xdp= dfp->To_Indx; xdp; xdp= xdp->GetNext()) + if (xdp->GetID() == id) + break; + + if (!xdp) { + sprintf(g->Message, "Wrong index ID %d", id); + return 0; + } // endif xdp + + // Allocate the key columns definition block + tdbp->Knum= xdp->GetNparts(); + tdbp->To_Key_Col= (PCOL*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PCOL)); + + // Get the key column description list + for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts(); kdp; kdp= (XKPDEF*)kdp->Next) + if (!(colp= tdbp->ColDB(g, kdp->Name, 0)) || colp->InitValue(g)) { + sprintf(g->Message, "Wrong column %s", kdp->Name); + return 0; + } else + tdbp->To_Key_Col[k++]= colp; + +#if defined(_DEBUG) + if (k != tdbp->Knum) { + sprintf(g->Message, "Key part number mismatch for %s", + xdp->GetName()); + return 0; + } // endif k +#endif // _DEBUG + + // Allocate the pseudo constants that will contain the key values + tdbp->To_Link= (PXOB*)PlugSubAlloc(g, NULL, tdbp->Knum * sizeof(PXOB)); + + for (k= 0, kdp= (XKPDEF*)xdp->GetToKeyParts(); + kdp; k++, kdp= (XKPDEF*)kdp->Next) { + cdp= tdbp->Key(k)->GetCdp(); + valp= AllocateValue(g, cdp->GetType(), cdp->GetLength()); + tdbp->To_Link[k]= new(g) CONSTANT(valp); + +//if (kdp->Klen && tdbp->To_Link[k]->GetResultType() == TYPE_STRING) +// ((XCOLBLK*)tdbp->To_Link[k])->SetLength(kdp->Klen); + +//((PCOL)tdbp->To_Link[k])->InitValue(g); + } // endfor k + + // Make the index on xdp + if (!xdp->IsAuto()) { + if (dfp->Huge) + pxp= new(g) XHUGE; + else + pxp= new(g) XFILE; + + if (tdbp->Knum == 1) // Single index + xp= new(g) XINDXS(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link); + else // Multi-Column index + xp= new(g) XINDEX(tdbp, xdp, pxp, tdbp->To_Key_Col, tdbp->To_Link); + + } else // Column contains same values as ROWID + xp= new(g) XXROW(tdbp); + + if (xp->Init(g)) + return 0; + + tdbp->To_Kindex= xp; + return (xp->IsMul()) ? 2 : 1; + } // end of CntIndexInit + +/***********************************************************************/ +/* IndexRead: fetch a record having the index value. */ +/***********************************************************************/ +RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, + const void *key, int len) + { + char *kp= (char*)key; + int n; + short lg; + RCODE rc; + PVAL valp; + PCOL colp; + XXBASE *xbp; + PTDBDOX tdbp; + + if (!ptdb) + return RC_FX; + if (!((PTDBASE)ptdb)->GetDef()->Indexable()) { + sprintf(g->Message, "Table %s is not indexable", ptdb->GetName()); + return RC_FX; + } else + tdbp= (PTDBDOX)ptdb; + + // Set reference values and index operator + if (!tdbp->To_Link || !tdbp->To_Kindex) { + sprintf(g->Message, "Index not initialized for table %s", tdbp->Name); + return RC_FX; + } else + xbp= (XXBASE*)tdbp->To_Kindex; + + if (key) { + for (n= 0; n < tdbp->Knum; n++) { + colp= (PCOL)tdbp->To_Key_Col[n]; + + if (colp->GetColUse(U_NULLS)) + kp++; // Skip null byte + + valp= tdbp->To_Link[n]->GetValue(); + + if (!valp->IsTypeNum()) { + if (colp->GetColUse(U_VAR)) { + lg= *(short*)kp; + kp+= sizeof(short); + valp->SetValue_char(kp, (int)lg); + } else + valp->SetValue_char(kp, valp->GetClen()); + + } else + valp->SetBinValue((void*)kp); + + kp+= valp->GetClen(); + + if (len == kp - (char*)key) { + n++; + break; + } else if (len < kp - (char*)key) { + strcpy(g->Message, "Key buffer is too small"); + return RC_FX; + } // endif len + + } // endfor n + + xbp->SetNval(n); + } // endif key + + xbp->SetOp(op); + xbp->SetNth(0); + + if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK) + rc= EvalColumns(g, tdbp); + + return rc; + } // end of CntIndexRead + +/***********************************************************************/ +/* Return the number of rows matching given values. */ +/***********************************************************************/ +int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len, + bool *incl, key_part_map *kmap) + { + const uchar *p, *kp; + int i, n, k[2]; + short lg; + bool b; + PVAL valp; + PCOL colp; + PTDBDOX tdbp; + XXBASE *xbp; + + if (!ptdb) + return -1; + else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) { + sprintf(g->Message, "Table %s is not indexable", ptdb->GetName()); + DBUG_PRINT("Range", ("%s", g->Message)); + return -1; + } else + tdbp= (PTDBDOX)ptdb; + + if (!tdbp->To_Link || !tdbp->To_Kindex) { + sprintf(g->Message, "Index not initialized for table %s", tdbp->Name); + DBUG_PRINT("Range", ("%s", g->Message)); + return -1; + } else + xbp= (XXBASE*)tdbp->To_Kindex; + + for (b= false, i= 0; i < 2; i++) { + p= kp= key[i]; + + if (kp) { + for (n= 0; n < tdbp->Knum; n++) { + if (kmap[i] & (key_part_map)(1 << n)) { + if (b == true) + // Cannot do indexing with missing intermediate key + return -1; + + colp= (PCOL)tdbp->To_Key_Col[n]; + + if (colp->GetColUse(U_NULLS)) + p++; // Skip null byte ??? + + valp= tdbp->To_Link[n]->GetValue(); + + if (!valp->IsTypeNum()) { + if (colp->GetColUse(U_VAR)) { + lg= *(short*)p; + p+= sizeof(short); + valp->SetValue_char((char*)p, (int)lg); + } else + valp->SetValue_char((char*)p, valp->GetClen()); + + } else + valp->SetBinValue((void*)p); + + if (xtrace) { + char bf[32]; + printf("i=%d n=%d key=%s\n", i, n, valp->GetCharString(bf)); + } // endif xtrace + + p+= valp->GetClen(); + + if (len[i] == p - kp) { + n++; + break; + } else if (len[i] < (unsigned)(p - kp)) { + strcpy(g->Message, "Key buffer is too small"); + return -1; + } // endif len + + } else + b= true; + + } // endfor n + + xbp->SetNval(n); + + if (xtrace) + printf("xbp=%p Nval=%d i=%d incl=%d\n", xbp, n, i, incl[i]); + + k[i]= xbp->Range(g, i + 1, incl[i]); + } else + k[i]= (i) ? xbp->GetNum_K() : 0; + + } // endfor i + + if (xtrace) + printf("k1=%d k0=%d\n", k[1], k[0]); + + return k[1] - k[0]; + } // end of CntIndexRange diff --git a/storage/connect/connect.cnf b/storage/connect/connect.cnf new file mode 100644 index 00000000000..a195e8d1a34 --- /dev/null +++ b/storage/connect/connect.cnf @@ -0,0 +1,2 @@ +[mariadb] +plugin-load-add=ha_connect.so diff --git a/storage/connect/connect.h b/storage/connect/connect.h new file mode 100644 index 00000000000..3fd52d283c6 --- /dev/null +++ b/storage/connect/connect.h @@ -0,0 +1,96 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/**************** Cnt H Declares Source Code File (.H) *****************/ +/* Name: CONNECT.H Version 2.4 */ +/* This file contains the some based classes declares. */ +/***********************************************************************/ +//#include "xtable.h" // Base class declares +#include "filamtxt.h" +#include "tabdos.h" + +//typedef struct _tabdesc *PTABD; // For friend setting +typedef struct _xinfo *PXF; +typedef struct _create_xinfo *PCXF; +typedef class ha_connect *PHC; +typedef class TDBDOX *PTDBDOX; + +/****************************************************************************/ +/* CONNECT functions called externally. */ +/****************************************************************************/ +bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname); +PTDB CntGetTDB(PGLOBAL g, const char *name, MODE xmod, PHC); +bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE, char *, char *, bool, PHC); +bool CntRewindTable(PGLOBAL g, PTDB tdbp); +int CntCloseTable(PGLOBAL g, PTDB tdbp); +int CntIndexInit(PGLOBAL g, PTDB tdbp, int id); +RCODE CntReadNext(PGLOBAL g, PTDB tdbp); +RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const void *k, int n); +RCODE CntWriteRow(PGLOBAL g, PTDB tdbp); +RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp); +RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all); +bool CntInfo(PGLOBAL g, PTDB tdbp, PXF info); +int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len, + bool *incl, key_part_map *kmap); + +/***********************************************************************/ +/* Definition of classes XCOLCRT, XIXDEF, XKPDEF, DOXDEF, TDBDOX */ +/* These classes purpose is chiefly to access protected items! */ +/***********************************************************************/ +class XCOLCRT: public COLCRT { + friend class ha_connect; + friend bool CntCreateTable(PGLOBAL, char *, PCXF); + public: + XCOLCRT(PSZ name) : COLCRT(name) {Nulls= -1;} // Constructor + bool HasNulls(void) {return (Nulls != 0);} + +private: + int Nulls; + }; // end of class XCOLCRT + +class DOXDEF: public DOSDEF { +//friend class TDBDOX; +//friend int MakeIndex(PGLOBAL, PTDB, PIXDEF); + friend int CntIndexInit(PGLOBAL, PTDB, int); + }; // end of class DOXDEF + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method base class declaration. */ +/***********************************************************************/ +class TDBDOX: public TDBDOS { + friend int MakeIndex(PGLOBAL, PTDB, PIXDEF); + friend int CntCloseTable(PGLOBAL, PTDB); + friend int CntIndexInit(PGLOBAL, PTDB, int); + friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const void*, int); + friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool); + friend int CntIndexRange(PGLOBAL, PTDB, const uchar**, uint*, + bool*, key_part_map*); + friend class ha_connect; + }; // end of class TDBDOX + +class XKPDEF: public KPARTDEF { + friend class TDBDOX; + friend class ha_connect; +//friend int CntMakeIndex(PGLOBAL, const char *, PIXDEF); + friend int CntIndexInit(PGLOBAL, PTDB, int); + public: + XKPDEF(const char *name, int n) : KPARTDEF((PSZ)name, n) {HasNulls= false;} + void SetNulls(bool b) {HasNulls= b;} + + protected: + bool HasNulls; /* Can have null values */ + }; // end of class XKPDEF + +//RCODE CheckRecord(PGLOBAL g, PTDB tdbp, char *oldbuf, char *newbuf); diff --git a/storage/connect/csort.cpp b/storage/connect/csort.cpp new file mode 100644 index 00000000000..6a2e7a33702 --- /dev/null +++ b/storage/connect/csort.cpp @@ -0,0 +1,962 @@ +/*************** CSort C Program Source Code File (.CPP) ***************/ +/* PROGRAM NAME: CSORT */ +/* ------------- */ +/* Version 2.2 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier Bertrand 1995-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program is the C++ sorting routines that use qsort/insert */ +/* algorithm and produces an offset/break table while sorting. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* csort.cpp - Source code */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* OS2DEF.LIB - OS2 libray definition subset. */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* Microsoft C++ Compiler */ +/* or GNU Compiler/Linker */ +/* or BORLAND 4.5 C++ compiler */ +/* */ +/* NOTE */ +/* ---- */ +/* These functions are not 64-bits ready. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +/***********************************************************************/ +/* Include application header files */ +/***********************************************************************/ +#include <stdlib.h> /* C standard library */ +#include <string.h> /* String manipulation declares */ +#include <stdio.h> /* Required for sprintf declare */ +#if defined(_DEBUG) +#include <assert.h> /* Assertion routine declares */ +#endif + +/***********************************************************************/ +/* Include CSort class header file */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" /* For MBLOCK type definition */ +#include "csort.h" /* CSort class definition */ + +#if !defined(BIGSORT) +#define BIGSORT 200000 +#endif // !BIGSORT + +/***********************************************************************/ +/* DB static external variables. */ +/***********************************************************************/ +extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ + +/***********************************************************************/ +/* Initialize the CSORT static members. */ +/***********************************************************************/ +int CSORT::Limit = 0; +double CSORT::Lg2 = log(2.0); +size_t CSORT::Cpn[1000] = {0}; /* Precalculated cmpnum values */ + +/***********************************************************************/ +/* CSORT constructor. */ +/***********************************************************************/ +CSORT::CSORT(bool cns, int th, int mth) + : Pex((int*&)Index.Memp), Pof((int*&)Offset.Memp) + { + G = NULL; + Dup =NULL; + Cons = cns; + Thresh = th; + Mthresh = mth; + Nitem = 0; + Index = Nmblk; + Offset = Nmblk; + Swix = NULL; + Savmax = 0; + Savcur = 0; + Savstep = NULL; + } // end of CSORT constructor + +/***********************************************************************/ +/* CSORT intialization. */ +/***********************************************************************/ +int CSORT::Qsort(PGLOBAL g, int nb) + { + int rc; + +#if defined(_DEBUG) + assert(Index.Size >= nb * sizeof(int)); +#endif + + if (nb > BIGSORT) { + G = g; + Dup = (PDBUSER)g->Activityp->Aptr; + + if (Dup->Proginfo) { + Savstep = Dup->Step; + Savmax = Dup->ProgMax; + Savcur = Dup->ProgCur; + + // Evaluate the number of comparisons that we will do + Dup->ProgMax = Cmpnum(nb); + Dup->ProgCur = 0; + Dup->Step = (char*)PlugSubAlloc(g, NULL, 32); + sprintf((char*)Dup->Step, MSG(SORTING_VAL), nb); + } else + Dup = NULL; + + } else + Dup = NULL; + + Nitem = nb; + + for (int n = 0; n < Nitem; n++) + Pex[n] = n; + + rc = (Cons) ? Qsortc() : Qsortx(); + + if (Dup) { + // Restore any change in progress info settings +// printf("Progcur=%u\n", Dup->ProgCur); + + Dup->Step = Savstep; + Dup->ProgMax = Savmax; + Dup->ProgCur = Savcur; + } // endif Subcor + + return rc; + } // end of QSort + +#if defined(DEBTRACE) +/***********************************************************************/ +/* Debug routine to be used by sort for specific data (dummy as now) */ +/***********************************************************************/ +void CSORT::DebugSort(int ph, int n, int *base, int *mid, int *tmp) + { + htrc("phase=%d n=%d base=%p mid=%p tmp=%p\n", + ph, n, base, mid, tmp); + } // end of DebugSort +#endif + +/***********************************************************************/ +/* Qsortx: Version adapted from qsortx.c by O.Bertrand */ +/* This version is specialy adapted for Index sorting, meaning that */ +/* the data is not moved, but the Index only is sorted. */ +/* Index array elements are any 4-byte word (a pointer or a int int */ +/* array index), they are not interpreted except by the user provided */ +/* comparison routine which must works accordingly. */ +/* In addition, this program takes care of data in which there is a */ +/* high rate of repetitions. */ +/* CAUTION: the sort algorithm used here is not conservative. Equal */ +/* values will be internally stored in unpredictable order. */ +/* The THRESHold below is the insertion sort threshold, and also the */ +/* threshold for continuing que quicksort partitioning. */ +/* The MTHREShold is where we stop finding a better median. */ +/* These two quantities should be adjusted dynamically depending upon */ +/* the repetition rate of the data. */ +/* Algorithm used: */ +/* First, set up some global parameters for Qstx to share. Then, */ +/* quicksort with Qstx(), and then a cleanup insertion sort ourselves. */ +/* Sound simple? It's not... */ +/***********************************************************************/ +int CSORT::Qsortx(void) + { + register int c; + register int lo, hi, min; + register int i, j, rc = 0; + // To do: rc should be checked for being used uninitialized + int *top; +#ifdef DEBTRACE + int ncp; + + num_comp = 0; +#endif + + /*********************************************************************/ + /* Prepare the Offset array that will be updated during sorts. */ + /*********************************************************************/ + if (Pof) + for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++) + Pof[j] = 0; + else + j = Nitem + 1; + + /*********************************************************************/ + /* Sort on one or zero element is obvious. */ + /*********************************************************************/ + if (Nitem <= 1) + return Nitem; + + /*********************************************************************/ + /* Thresh seems to be good as (10 * n / rep). But for testing we */ + /* set it directly as one parameter of the Xset function call. */ + /* Note: this should be final as the rep parameter is no more used. */ + /*********************************************************************/ + top = Pex + Nitem; + +#ifdef DEBTRACE + htrc("Qsortx: nitem=%d thresh=%d mthresh=%d\n", + Nitem, Thresh, Mthresh); +#endif + + /*********************************************************************/ + /* If applicable, do a rough preliminary quick sort. */ + /*********************************************************************/ + if (Nitem >= Thresh) + Qstx(Pex, top); + +#ifdef DEBTRACE + htrc(" after quick sort num_comp=%d\n", num_comp); + ncp = num_comp; + num_comp = 0; +#ifdef DEBUG2 + DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL); +#endif +#endif + + if (Thresh > 2) { + if (Pof) + /*****************************************************************/ + /* The preliminary search for the smallest element has been */ + /* removed so with no sentinel in place, we must check for x */ + /* going below the Pof pointer. For each remaining element */ + /* group from [1] to [n-1], set hi to the index of the element */ + /* AFTER which this one goes. Then, do the standard insertion */ + /* sort shift on an integer at a time basis for each equal */ + /* element group in the frob. */ + /*****************************************************************/ + for (min = hi = 0; min < Nitem; min = hi) { + if (Pof[hi]) { + hi += Pof[hi]; + continue; + } // endif Pof + + Pof[min] = 1; + +#ifdef DEBUG2 + htrc("insert from min=%d\n", min); +#endif + + for (lo = hi; !Pof[++hi]; lo = hi) { + while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0) + if (Pof[lo] > 0) + lo -= Pof[lo]; + else + return -2; + + if (++lo != hi) { + c = Pex[hi]; + + for (i = j = hi; i > 0; i = j) + if (Pof[i - 1] <= 0) + return -3; + else if ((j -= Pof[i - 1]) >= lo) { + Pex[i] = Pex[j]; + Pof[j + 1] = Pof[i] = Pof[j]; + } else + break; + + Pex[i] = c; + } // endif lo + + if (rc) + Pof[lo] = 1; + else { + i = lo - Pof[lo - 1]; + Pof[lo] = ++Pof[i]; + } // endelse + +#ifdef DEBUG2 + htrc("rc=%d lo=%d hi=%d trx=%d\n", rc, lo, hi, Pof[lo]); +#endif + + } // endfor hi + + } // endfor min + + else + /*****************************************************************/ + /* Call conservative insertion sort not using/setting offset. */ + /*****************************************************************/ + Istc(Pex, Pex + min(Nitem, Thresh), top); + + } // endif Thresh + +#ifdef DEBTRACE + htrc(" after insert sort num_comp=%d\n", num_comp); + num_comp += ncp; +#endif + + if (Pof) + /*******************************************************************/ + /* Reduce the Offset array. */ + /*******************************************************************/ + for (i = j = 0; i <= Nitem; j++, i += c) { +#ifdef DEBUG2 + htrc(" trxp(%d)=%d trxp(%d)=%d c=%d\n", + i, Pof[i], j, Pof[j], c); +#endif + if ((c = Pof[i])) + Pof[j] = i; + else + return -4; + + } // endfor i + + return (j - 1); + } // end of Qsortx + +/***********************************************************************/ +/* Qstx: Do a quicksort on index elements (just one int int). */ +/* First, find the median element, and put that one in the first place */ +/* as the discriminator. (This "median" is just the median of the */ +/* first, last and middle elements). (Using this median instead of */ +/* the first element is a big win). Then, the usual partitioning/ */ +/* swapping, followed by moving the discriminator into the right place.*/ +/* Element equal to the discriminator are placed against it, so the */ +/* mid (discriminator) block grows when equal elements exist. This is */ +/* a huge win in case of repartitions with few different elements. */ +/* The mid block being at its final position, its first and last */ +/* elements are marked in the offset list (used to make break list). */ +/* Then, figure out the sizes of the two partitions, do the smaller */ +/* one recursively and the larger one via a repeat of this code. */ +/* Stopping when there are less than THRESH elements in a partition */ +/* and cleaning up with an insertion sort (in our caller) is a huge */ +/* win(?). All data swaps are done in-line, which is space-losing but */ +/* time-saving. (And there are only three places where this is done). */ +/***********************************************************************/ +void CSORT::Qstx(int *base, int *max) + { + register int *i, *j, *jj, *mid, *him, c; + int *tmp; + int lo, hi, rc; + size_t zlo, zhi, cnm; + + zlo = zhi = cnm = 0; // Avoid warning message + + lo = max - base; // Number of elements as longs + + if (Dup) + cnm = Cmpnum(lo); + + do { + /*******************************************************************/ + /* At the top here, lo is the number of integers of elements in */ + /* the current partition. (Which should be max - base). */ + /* Find the median of the first, last, and middle element and make */ + /* that the middle element. Set j to largest of first and middle. */ + /* If max is larger than that guy, then it's that guy, else */ + /* compare max with loser of first and take larger. Things are */ + /* set up to prefer the middle, then the first in case of ties. */ + /* In addition, hi and rc are set to comparison results. So if hi */ + /* is null, the two high values are equal and if rc is null, the */ + /* two low values are equal. This was used to set which test will */ + /* be made by LE and which one by LT (does not apply anymore). */ + /*******************************************************************/ + him = mid = i = base + (lo >> 1); + hi = rc = 0; + +#ifdef DEBTRACE + tmp = max - 1; + htrc("--> block base=%d size=%d\n", base - Pex, lo); + DebugSort(2, 0, base, mid, tmp); +#endif + + if (lo >= Mthresh) { + rc = Qcompare((jj = base), i); + j = (rc > 0) ? jj : i; + hi = Qcompare(j, (tmp = max - 1)); + + if (hi > 0 && rc) { + j = (j == jj) ? i : jj; // switch to first loser + + if ((rc = Qcompare(j, tmp)) < 0) + j = tmp; + + } // endif + + if (j != i) { + c = *i; + *i = *j; + *j = c; + } // endif j + + } else if (lo == 2) { + /*****************************************************************/ + /* Small group. Do special quicker processing. */ + /*****************************************************************/ + if ((rc = Qcompare(base, (him = base + 1))) > 0) + c = *base, *base = *him, *him = c; + + if (Pof) + Pof[base - Pex] = Pof[him - Pex] = (rc) ? 1 : 2; + + break; + } // endif lo + +#ifdef DEBTRACE + DebugSort(3, hi, NULL, mid, &rc); +#endif + + /*******************************************************************/ + /* Semi-standard quicksort partitioning/swapping. Added here is */ + /* a test on equality. All values equal to the mid element are */ + /* placed under or over it. Mid block can be also moved when it */ + /* is necessary because the other partition is full. At the end */ + /* of the for loop the mid block is definitely positionned. */ + /*******************************************************************/ + for (i = base, j = max - 1; ;) { + CONT: + while (i < mid) + if ((rc = Qcompare(i, mid)) < 0) + i++; + else if (!rc) { + c = *i; + *i = *(--mid); + *mid = c; + } else + break; + + while (j > him) + if ((rc = Qcompare(him, j)) < 0) + j--; + else if (!rc) { + c = *j; + *j = *(++him); + *him = c; + } else if (i == mid) { // Triple move: + c = *j; // j goes under mid block + *j = *(++him); // val over mid block -> j + *him = *mid++; // and mid block goes one + *i++ = c; // position higher. + } else { // i <-> j + c = *i; + *i++ = *j; + *j-- = c; + goto CONT; + } // endif's + + if (i == mid) + break; + else { // Triple move: + c = *i; // i goes over mid block + *i = *(--mid); // val under mid block -> i + *mid = *him--; // and mid block goes one + *j-- = c; // position lower. + } // endelse + + } // endfor i + + /*******************************************************************/ + /* The mid block being placed at its final position we can now set */ + /* the offset array values indicating break point and block size. */ + /*******************************************************************/ + j = mid; + i = him + 1; + + if (Pof) + Pof[him - Pex] = Pof[mid - Pex] = i - j; + + /*******************************************************************/ + /* Look at sizes of the two partitions, do the smaller one first */ + /* by recursion, then do the larger one by making sure lo is its */ + /* size, base and max are update correctly, and branching back. */ + /* But only repeat (recursively or by branching) if the partition */ + /* is of at least size THRESH. */ + /*******************************************************************/ + lo = j - base; + hi = max - i; + + if (Dup) { // Update progress information + zlo = Cmpnum(lo); + zhi = Cmpnum(hi); + Dup->ProgCur += cnm - (zlo + zhi); + } // endif Dup + +#ifdef DEBTRACE + htrc(" done lo=%d sep=%d hi=%d\n", lo, i - j, hi); +#endif + + if (lo <= hi) { + if (lo >= Thresh) + Qstx(base, j); + else if (lo == 1 && Pof) + Pof[base - Pex] = 1; + + base = i; + lo = hi; + cnm = zhi; + } else { + if (hi >= Thresh) + Qstx(i, max); + else if (hi == 1 && Pof) + Pof[i - Pex] = 1; + + max = j; + cnm = zlo; + } // endif + + if (lo == 1 && Pof) + Pof[base - Pex] = 1; + + } while (lo >= Thresh); // enddo + + } // end of Qstx + +/***********************************************************************/ +/* Qsortc.c: Version adapted from qsort.c by O.Bertrand */ +/* This version is specialy adapted for Index sorting, meaning that */ +/* the data is not moved, but the Index only is sorted. */ +/* Index array elements are any 4-byte word (a pointer or a int int */ +/* array index), they are not interpreted except by the user provided */ +/* comparison routine which must works accordingly. */ +/* In addition, this program takes care of data in which there is a */ +/* high rate of repetitions. */ +/* NOTE: the sort algorithm used here is conservative. Equal and */ +/* greater than values are internally stored in additional work area. */ +/* The THRESHold below is the insertion sort threshold, and also the */ +/* threshold for continuing que quicksort partitioning. */ +/* The MTHREShold is where we stop finding a better median. */ +/* These two quantities should be adjusted dynamically depending upon */ +/* the repetition rate of the data. */ +/* Algorithm used: */ +/* First, set up some global parameters for Qstc to share. Then, */ +/* quicksort with Qstc(), and then a cleanup insertion sort ourselves.*/ +/* Sound simple? It's not... */ +/***********************************************************************/ +int CSORT::Qsortc(void) + { + register int c; + register int lo, hi, min; + register int i, j, k, m, rc = 0; + // To do: rc should be checked for being used uninitialized + int *max; +#ifdef DEBTRACE + int ncp; + + num_comp = 0; +#endif + + /*********************************************************************/ + /* Prepare the Offset array that will be updated during sorts. */ + /*********************************************************************/ + if (Pof) + for (Pof[Nitem] = Nitem, j = 0; j < Nitem; j++) + Pof[j] = 0; + else + j = Nitem + 1; + + /*********************************************************************/ + /* Sort on one or zero element is obvious. */ + /*********************************************************************/ + if (Nitem <= 1) + return Nitem; + + /*********************************************************************/ + /* Thresh seems to be good as (10 * n / rep). But for testing we */ + /* set it directly as one parameter of the Xset function call. */ + /* Note: this should be final as the rep parameter is no more used. */ + /*********************************************************************/ + max = Pex + Nitem; + +#ifdef DEBTRACE + htrc("Qsortc: nitem=%d thresh=%d mthresh=%d\n", + Nitem, Thresh, Mthresh); +#endif + + /*********************************************************************/ + /* If applicable, do a rough preliminary conservative quick sort. */ + /*********************************************************************/ + if (Nitem >= Thresh) { + if (!(Swix = (int *)malloc(Nitem * sizeof(int)))) + return -1; + + Qstc(Pex, max); + + free(Swix); + Swix = NULL; + } // endif n + +#ifdef DEBTRACE + htrc(" after quick sort num_comp=%d\n", num_comp); + ncp = num_comp; + num_comp = 0; +#ifdef DEBUG2 + DebugSort((Pof) ? 1 : 4, Nitem, Pex, NULL, NULL); +#endif +#endif + + if (Thresh > 2) { + if (Pof) + /*****************************************************************/ + /* The preliminary search for the smallest element has been */ + /* removed so with no sentinel in place, we must check for x */ + /* going below the Pof pointer. For each remaining element */ + /* group from [1] to [n-1], set hi to the index of the element */ + /* AFTER which this one goes. Then, do the standard insertion */ + /* sort shift on an integer at a time basis for each equal */ + /* element group in the frob. */ + /*****************************************************************/ + for (min = hi = 0; min < Nitem; min = hi) { + if (Pof[hi]) { + hi += Pof[hi]; + continue; + } // endif + + Pof[min] = 1; + +#ifdef DEBUG2 + htrc("insert from min=%d\n", min); +#endif + + for (lo = hi; !Pof[++hi]; lo = hi) { + while (lo >= min && (rc = Qcompare(Pex + lo, Pex + hi)) > 0) + if (Pof[lo] > 0) + lo -= Pof[lo]; + else + return -2; + + if (++lo != hi) { + c = Pex[hi]; + + for (i = j = hi; i > 0; i = j) + if (Pof[i - 1] <= 0) + return -3; + else if ((j -= Pof[i - 1]) >= lo) { + for (k = m = i; --m >= j; k--) // Move intermediate + Pex[k] = Pex[m]; // for conservation. + + Pof[j + 1] = Pof[i] = Pof[j]; + } else + break; + + Pex[i] = c; + } // endif + + if (rc) + Pof[lo] = 1; + else { + i = lo - Pof[lo - 1]; + Pof[lo] = ++Pof[i]; + } // endelse + +#ifdef DEBUG2 + htrc("rc=%d lo=%d hi=%d ofx=%d\n", rc, lo, hi, Pof[lo]); +#endif + + } // endfor hi + + } // endfor min + + else + /*****************************************************************/ + /* Call conservative insertion sort not using/setting offset. */ + /*****************************************************************/ + Istc(Pex, Pex + min(Nitem, Thresh), max); + + } // endif Thresh + +#ifdef DEBTRACE + htrc(" after insert sort num_comp=%d\n", num_comp); + num_comp += ncp; +#endif + + if (Pof) + /*******************************************************************/ + /* Reduce the Offset array. */ + /*******************************************************************/ + for (i = j = 0; i <= Nitem; j++, i += c) { +#ifdef DEBUG2 + htrc(" Pof(%d)=%d Pof(%d)=%d c=%d\n", + i, Pof[i], j, Pof[j], c); +#endif + if ((c = Pof[i])) + Pof[j] = i; + else + return -4; + + } // endfor i + + return (j - 1); + } // end of Qsortc + +/***********************************************************************/ +/* Qstc: Do a quicksort on index elements (just one int int). */ +/* First, find the median element, and set it as the discriminator. */ +/* (This "median" is just the median of the first, last and middle */ +/* elements). (Using this median instead of the first element is a */ +/* big win). Then, the special partitioning/swapping, where elements */ +/* smaller than the discriminator are placed in the sorted block, */ +/* elements equal to the discriminator are placed backward from the */ +/* top of the work area and elements greater than *j (discriminator) */ +/* are placed in the work area from its bottom. Then the elements in */ +/* the work area are placed back in the sort area in natural order, */ +/* making the sort conservative. Non equal blocks shrink faster when */ +/* equal elements exist. This is a huge win in case of repartitions */ +/* with few different elements. The mid block being at its final */ +/* position, its first and last elements are marked in the offset */ +/* list (used to make break list). Then, figure out the sizes of the */ +/* two partitions, do the smaller one recursively and the larger one */ +/* via a repeat of this code. Stopping when there are less than */ +/* THRESH elements in a partition and cleaning up with an insertion */ +/* sort (in our caller) is a huge win (yet to be proved?). */ +/***********************************************************************/ +void CSORT::Qstc(int *base, int *max) + { + register int *i, *j, *jj, *lt, *eq, *gt, *mid; + int c, lo, hi, rc; + size_t zlo, zhi, cnm; + + zlo = zhi = cnm = 0; // Avoid warning message + + lo = max - base; // Number of elements as longs + + if (Dup) + cnm = Cmpnum(lo); + + do { + /*******************************************************************/ + /* At the top here, lo is the number of integers of elements in */ + /* the current partition. (Which should be max - base). Find the */ + /* median of the first, last, and middle element and make that */ + /* the compare element. Set jj to smallest of middle and last. */ + /* If base is smaller or equal than that guy, then it's that guy, */ + /* else compare base with loser of first and take smaller. Things */ + /* are set up to prefer the top, then the middle in case of ties. */ + /*******************************************************************/ + i = base + (lo >> 1); + jj = mid = max - 1; + +#ifdef DEBTRACE + htrc("--> block base=%d size=%d\n", base - Pex, lo); + DebugSort(2, 0, base, i, mid); +#endif + + if (lo >= Mthresh) { + jj = ((rc = Qcompare(i, mid)) < 0) ? i : mid; + + if (rc && Qcompare(base, jj) > 0) { + jj = (jj == mid) ? i : mid; // switch to first loser + + if (Qcompare(base, jj) < 0) + jj = base; + + } // endif + + if (jj != mid) { + /***************************************************************/ + /* The compare element must be at the top of the block so it */ + /* cannot be overwritten while making the partitioning. So */ + /* save the last block value which will be compared later. */ + /***************************************************************/ + c = *mid; + *mid = *jj; + } // endif + + } else if (lo == 2) { + /*****************************************************************/ + /* Small group. Do special quicker processing. */ + /*****************************************************************/ + if ((rc = Qcompare(base, (i = base + 1))) > 0) + c = *base, *base = *i, *i = c; + + if (Pof) + Pof[base - Pex] = Pof[i - Pex] = (rc) ? 1 : 2; + + break; + } // endif lo + +#ifdef DEBTRACE + DebugSort(3, lo, NULL, jj, &rc); +#endif + + /*******************************************************************/ + /* Non-standard quicksort partitioning using additional storage */ + /* to store values less than, equal or greater than the middle */ + /* element. This uses more memory but provides conservation of */ + /* the equal elements order. */ + /*******************************************************************/ + lt = base; + eq = Swix + lo; + gt = Swix; + + if (jj == mid) { + /*****************************************************************/ + /* Compare element was last. No problem. */ + /*****************************************************************/ + for (i = base; i < max; i++) + if ((rc = Qcompare(i, mid)) < 0) + *lt++ = *i; + else if (rc > 0) + *gt++ = *i; + else + *--eq = *i; + + } else { + /*****************************************************************/ + /* Compare element was not last and was copied to top of block. */ + /*****************************************************************/ + for (i = base; i < mid; i++) + if ((rc = Qcompare(i, mid)) < 0) + *lt++ = *i; + else if (rc > 0) + *gt++ = *i; + else + *--eq = *i; + + /*****************************************************************/ + /* Restore saved last value and do the comparison from there. */ + /*****************************************************************/ + *--i = c; + + if ((rc = Qcompare(i, mid)) < 0) + *lt++ = *i; + else if (rc > 0) + *gt++ = *i; + else + *--eq = *i; + + } // endif + + /*******************************************************************/ + /* Now copy the equal and greater values back in the main array in */ + /* the same order they have been placed in the work area. */ + /*******************************************************************/ + for (j = Swix + lo, i = lt; j > eq; ) + *i++ = *--j; + + for (j = Swix, jj = i; j < gt; ) + *i++ = *j++; + + /*******************************************************************/ + /* The mid block being placed at its final position we can now set */ + /* the offset array values indicating break point and block size. */ + /*******************************************************************/ + if (Pof) + Pof[lt - Pex] = Pof[(jj - 1) - Pex] = jj - lt; + + /*******************************************************************/ + /* Look at sizes of the two partitions, do the smaller one first */ + /* by recursion, then do the larger one by making sure lo is its */ + /* size, base and max are update correctly, and branching back. */ + /* But only repeat (recursively or by branching) if the partition */ + /* is of at least size THRESH. */ + /*******************************************************************/ + lo = lt - base; + hi = gt - Swix; + + if (Dup) { // Update progress information + zlo = Cmpnum(lo); + zhi = Cmpnum(hi); + Dup->ProgCur += cnm - (zlo + zhi); + } // endif Dup + +#ifdef DEBTRACE + htrc(" done lo=%d hi=%d\n", + lo, /*Swix + lt - base - eq,*/ hi); +#endif + + if (lo <= hi) { + if (lo >= Thresh) + Qstc(base, lt); + else if (lo == 1 && Pof) + Pof[base - Pex] = 1; + + base = jj; + lo = hi; + cnm = zhi; + } else { + if (hi >= Thresh) + Qstc(jj, max); + else if (hi == 1 && Pof) + Pof[jj - Pex] = 1; + + max = lt; + cnm = zlo; + } // endif + + if (lo == 1 && Pof) + Pof[base - Pex] = 1; + + } while (lo >= Thresh); // enddo + + } // end of Qstc + +/***********************************************************************/ +/* Conservative insertion sort not using/setting offset array. */ +/***********************************************************************/ +void CSORT::Istc(int *base, int *hi, int *max) + { + register int c; + register int *lo; + register int *i, *j; + + /*********************************************************************/ + /* First put smallest element, which must be in the first THRESH, */ + /* in the first position as a sentinel. This is done just by */ + /* searching the 1st THRESH elements (or the 1st n if n < THRESH) */ + /* finding the min, and shifting it into the first position. */ + /*********************************************************************/ + for (j = lo = base; ++lo < hi; ) + if (Qcompare(j, lo) > 0) + j = lo; + + if (j != base) { // shift j into place + c = *j; + + for (i = j; --j >= base; i = j) + *i = *j; + + *base = c; + } // endif j + +#ifdef DEBTRACE + htrc("sentinel %d in place, base=%p hi=%p max=%p\n", + c, base, hi, max); +#endif + + /*********************************************************************/ + /* With our sentinel in place, we now run the following hyper- */ + /* fast insertion sort. For each remaining element, lo, from [1] */ + /* to [n-1], set hi to the index of the element AFTER which this */ + /* one goes. Then, do the standard insertion sort shift for each */ + /* element in the frob. */ + /*********************************************************************/ + for (lo = base; (hi = ++lo) < max;) { + while (Qcompare(--hi, lo) > 0) ; + +#ifdef DEBUG2 + htrc("after while: hi(%p)=%d lo(%p)=%d\n", + hi, *hi, lo, *lo); +#endif + + if (++hi != lo) { + c = *lo; + + for (i = j = lo; --j >= hi; i = j) + *i = *j; + + *i = c; + } // endif hi + + } // endfor lo + + } // end of Istc + +/* -------------------------- End of CSort --------------------------- */ diff --git a/storage/connect/csort.h b/storage/connect/csort.h new file mode 100644 index 00000000000..702152de43e --- /dev/null +++ b/storage/connect/csort.h @@ -0,0 +1,106 @@ +/*************** Csort H Declares Source Code File (.H) ****************/ +/* Name: CSORT.H Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2000-2012 */ +/* */ +/* This file contains the CSORT class declares (not 64-bits ready) */ +/* */ +/* Note on use of this class: This class is meant to be used as a */ +/* base class by the calling class. This is because the comparison */ +/* routine must belong to both CSORT and the calling class. */ +/* This avoids to pass explicitly to it the calling "this" pointer. */ +/***********************************************************************/ +#if !defined(CSORT_DEFINED) +#define CSORT_DEFINED + +#include <math.h> /* Required for log function */ +#undef DOMAIN // Was defined in math.h + +/***********************************************************************/ +/* Constant and external definitions. */ +/***********************************************************************/ +#define THRESH 4 /* Threshold for insertion (was 4) */ +#define MTHRESH 6 /* Threshold for median */ + +#ifdef DEBTRACE +extern FILE *debug; /* Debug file */ +#endif + +typedef int* const CPINT; + +/***********************************************************************/ +/* This is the CSORT base class declaration. */ +/***********************************************************************/ +class DllExport CSORT { + public: + // Constructor + CSORT(bool cns, int th = THRESH, int mth = MTHRESH); + + protected: + // Implementation + /*********************************************************************/ + /* qsortx/qstx are NOT conservative but use less storage space. */ + /* qsortc/qstc ARE conservative but use more storage space. */ + /*********************************************************************/ + int Qsortx(void); /* Index quick/insert sort */ + void Qstx(int *base, int *max); /* Preliminary quick sort */ + int Qsortc(void); /* Conservative q/ins sort */ + void Qstc(int *base, int *max); /* Preliminary quick sort */ + void Istc(int *base, int *hi, int *max); /* Insertion sort routine */ + + public: + // Methods + int Qsort(PGLOBAL g, int n); /* Sort calling routine */ +//virtual void Print(PGLOBAL g, FILE *f, uint n); +//virtual void Print(PGLOBAL g, char *ps, uint z); +#ifdef DEBTRACE + int GetNcmp(void) {return num_comp;} +#endif + + protected: + // Overridable + virtual int Qcompare(int *, int *) = 0; /* Item compare routine */ +#ifdef DEBTRACE + virtual void DebugSort(int ph, int n, int *base, int *mid, int *tmp); +#endif + + public: + // Utility + static void SetCmpNum(void) + {for (int i = 1; i < 1000; i++) Cpn[i] = Cmpnum(i); Limit = 1000;} + protected: + static size_t Cmpnum(int n) +#if defined(AIX) + {return (n < Limit) ? Cpn[n] + : (size_t)round(1.0 + (double)n * (log2((double)n) - 1.0));} +#else // !AIX + {return (n < Limit) ? Cpn[n] + : (size_t)(1.5 + (double)n * (log((double)n)/Lg2 - 1.0));} +#endif // !AIX + + + // Members + static int Limit; /* Size of precalculated array */ + static size_t Cpn[1000]; /* Precalculated cmpnum values */ + static double Lg2; /* Precalculated log(2) value */ + PGLOBAL G; + PDBUSER Dup; /* Used for progress info */ + bool Cons; /* true for conservative sort */ + int Thresh; /* Threshold for using qsort */ + int Mthresh; /* Threshold for median find */ + int Nitem; /* Number of items to sort */ + MBLOCK Index; /* Index allocation block */ + MBLOCK Offset; /* Offset allocation block */ + CPINT &Pex; /* Reference to sort index */ + CPINT &Pof; /* Reference to offset array */ + int *Swix; /* Pointer on EQ/GT work area */ + int Savmax; /* Saved ProgMax value */ + int Savcur; /* Saved ProgCur value */ + LPCSTR Savstep; /* Saved progress step */ +#ifdef DEBTRACE + int num_comp; /* Number of quick sort calls */ +#endif + }; // end of class CSORT + +#endif // CSORT_DEFINED + diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp new file mode 100644 index 00000000000..95426be97bc --- /dev/null +++ b/storage/connect/domdoc.cpp @@ -0,0 +1,631 @@ +/******************************************************************/ +/* Implementation of XML document processing using MS DOM */ +/* Author: Olivier Bertrand 2007 - 2013 */ +/******************************************************************/ +#include "my_global.h" +#include <stdio.h> +#if defined(WIN32) +//#include <windows.h> +#if defined(MSX2) +#import "msxml2.dll" //Does not exist on Vista +#elif defined(MSX3) +#import "msxml3.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#elif defined(MSX4) +#import "msxml4.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#elif defined(MSX6) +#import "msxml6.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ?? +#else // MSX4 +#error MSX? is not defined +#endif // MSX +using namespace MSXML2; +#else +#error This is a Windows implementation only +#endif + +#define NODE_TYPE_LIST + +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "domdoc.h" + +inline bool TestHr(PGLOBAL g, HRESULT hr) + { + if FAILED(hr) { + sprintf(g->Message, "%s, hr=%d", MSG(COM_ERROR), hr); + return true; + } else + return false; + + } // end of TestHr + +/******************************************************************/ +/* Return a DOMDOC as a XMLDOC. */ +/******************************************************************/ +PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + return (PXDOC) new(g) DOMDOC(nsl, nsdf, enc, fp); + } // end of GetDomDoc + +/***********************************************************************/ +/* Close a loaded DOM XML file. */ +/***********************************************************************/ +void CloseXMLFile(PGLOBAL g, PFBLOCK fp, bool all) + { + PXBLOCK xp = (PXBLOCK)fp; + + if (xp && xp->Count > 1 && !all) { + xp->Count--; + } else if (xp && xp->Count > 0) { + try { + xp->Docp->Release(); + } catch(_com_error e) { + sprintf(g->Message, "%s %s", MSG(COM_ERROR), e.Description()); + } catch(...) {} + + CoUninitialize(); + xp->Count = 0; + } // endif + + } // end of CloseXMLFile + +/* ------------------------ class DOMDOC ------------------------ */ + +/******************************************************************/ +/* DOMDOC constructor. */ +/******************************************************************/ +DOMDOC::DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) + : XMLDOCUMENT(nsl, nsdf, enc) + { + assert (!fp || fp->Type == TYPE_FB_XML); + Docp = (fp) ? ((PXBLOCK)fp)->Docp : NULL; + Nlist = NULL; + Hr = 0; + } // end of DOMDOC constructor + +/******************************************************************/ +/* Initialize XML parser and check library compatibility. */ +/******************************************************************/ +bool DOMDOC::Initialize(PGLOBAL g) + { + if (TestHr(g, CoInitialize(NULL))) + return true; + + if (TestHr(g, Docp.CreateInstance("msxml2.domdocument"))) + return true; + + return MakeNSlist(g); + } // end of Initialize + +/******************************************************************/ +/* Parse the XML file and construct node tree in memory. */ +/******************************************************************/ +bool DOMDOC::ParseFile(char *fn) + { + // Load the document + Docp->async = false; + + if (!(bool)Docp->load((_bstr_t)fn)) + return true; + + return false; + } // end of ParseFile + +/******************************************************************/ +/* Create or reuse an Xblock for this document. */ +/******************************************************************/ +PFBLOCK DOMDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + PXBLOCK xp = (PXBLOCK)PlugSubAlloc(g, NULL, sizeof(XBLOCK)); + + memset(xp, 0, sizeof(XBLOCK)); + xp->Next = (PXBLOCK)dup->Openlist; + dup->Openlist = (PFBLOCK)xp; + xp->Type = TYPE_FB_XML; + xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1); + strcpy((char*)xp->Fname, fn); + xp->Count = 1; + xp->Length = (m == MODE_READ) ? 1 : 0; + xp->Docp = Docp; + xp->Retcode = rc; + + // Return xp as a fp + return (PFBLOCK)xp; + } // end of LinkXblock + +/******************************************************************/ +/* Create the XML node. */ +/******************************************************************/ +bool DOMDOC::NewDoc(PGLOBAL g, char *ver) + { + char buf[64]; + MSXML2::IXMLDOMProcessingInstructionPtr pip; + + sprintf(buf, "version=\"%s\" encoding=\"%s\"", ver, Encoding); + pip = Docp->createProcessingInstruction("xml", buf); + return(TestHr(g, Docp->appendChild(pip))); + } // end of NewDoc + +/******************************************************************/ +/* Add a comment to the document node. */ +/******************************************************************/ +void DOMDOC::AddComment(PGLOBAL g, char *com) + { + TestHr(g, Docp->appendChild(Docp->createComment(com))); + } // end of AddComment + +/******************************************************************/ +/* Return the node class of the root of the document. */ +/******************************************************************/ +PXNODE DOMDOC::GetRoot(PGLOBAL g) + { + MSXML2::IXMLDOMElementPtr root = Docp->documentElement; + + if (root == NULL) + return NULL; + + return new(g) DOMNODE(this, root); + } // end of GetRoot + +/******************************************************************/ +/* Create a new root element and return its class node. */ +/******************************************************************/ +PXNODE DOMDOC::NewRoot(PGLOBAL g, char *name) + { + MSXML2::IXMLDOMElementPtr ep = Docp->createElement(name); + + if (ep == NULL || TestHr(g, Docp->appendChild(ep))) + return NULL; + + return new(g) DOMNODE(this, ep); + } // end of NewRoot + +/******************************************************************/ +/* Return a void DOMNODE node class. */ +/******************************************************************/ +PXNODE DOMDOC::NewPnode(PGLOBAL g, char *name) + { + MSXML2::IXMLDOMElementPtr root = NULL; + + if (name) + if ((root = Docp->createElement(name)) == NULL) + return NULL; + + return new(g) DOMNODE(this, root); + } // end of NewPnode + +/******************************************************************/ +/* Return a void DOMATTR node class. */ +/******************************************************************/ +PXATTR DOMDOC::NewPattr(PGLOBAL g) + { + return new(g) DOMATTR(this, NULL); + } // end of NewPattr + +/******************************************************************/ +/* Return a void DOMATTR node class. */ +/******************************************************************/ +PXLIST DOMDOC::NewPlist(PGLOBAL g) + { + return new(g) DOMNODELIST(this, NULL); + } // end of NewPlist + +/******************************************************************/ +/* Dump the node tree to a new XML file. */ +/******************************************************************/ +int DOMDOC::DumpDoc(PGLOBAL g, char *ofn) + { + int rc = 0; + + try { + Docp->save(ofn); + } catch(_com_error e) { + sprintf(g->Message, "%s: %s", MSG(COM_ERROR), + _com_util::ConvertBSTRToString(e.Description())); + rc = -1; + } catch(...) {} + + return rc; + } // end of Dump + +/******************************************************************/ +/* Free the document, cleanup the XML library, and */ +/* debug memory for regression tests. */ +/******************************************************************/ +void DOMDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) + { + CloseXMLFile(g, xp, false); + } // end of Close + +/* ----------------------- class DOMNODE ------------------------ */ + +/******************************************************************/ +/* DOMNODE constructor. */ +/******************************************************************/ +DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp) + { + Docp = ((PDOMDOC)dp)->Docp; + Nodep = np; + Ws = NULL; + Len = 0; + } // end of DOMNODE constructor + +/******************************************************************/ +/* Return the node name. */ +/******************************************************************/ +char *DOMNODE::GetName(PGLOBAL g) + { + if (!WideCharToMultiByte(CP_ACP, 0, Nodep->nodeName, -1, + Name, sizeof(Name), NULL, NULL)) { + strcpy(g->Message, MSG(NAME_CONV_ERR)); + return NULL; + } // endif + + return Name; + } // end of GetName + +/******************************************************************/ +/* Return the node class of next sibling of the node. */ +/******************************************************************/ +PXNODE DOMNODE::GetNext(PGLOBAL g) + { + if (Nodep->nextSibling == NULL) + Next = NULL; + else if (!Next) + Next = new(g) DOMNODE(Doc, Nodep->nextSibling); + + return Next; + } // end of GetNext + +/******************************************************************/ +/* Return the node class of first children of the node. */ +/******************************************************************/ +PXNODE DOMNODE::GetChild(PGLOBAL g) + { + if (Nodep->firstChild == NULL) + Children = NULL; + else if (!Children) + Children = new(g) DOMNODE(Doc, Nodep->firstChild); + + return Children; + } // end of GetChild + +/******************************************************************/ +/* Return the content of a node and subnodes. */ +/******************************************************************/ +RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + + // Nodep can be null for a missing HTML table column + if (Nodep) { + if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1, + buf, len, NULL, NULL)) { + DWORD lsr = GetLastError(); + + switch (lsr) { + case 0: + case ERROR_INSUFFICIENT_BUFFER: // 122L + sprintf(g->Message, "Truncated %s content", GetName(g)); + rc = RC_INFO; + break; + case ERROR_NO_UNICODE_TRANSLATION: // 1113L + sprintf(g->Message, "Invalid character(s) in %s content", + GetName(g)); + rc = RC_INFO; + break; + default: + sprintf(g->Message, "System error getting %s content", + GetName(g)); + rc = RC_FX; + break; + } // endswitch + + } // endif + + } else + *buf = '\0'; + + return rc; + } // end of GetContent + +/******************************************************************/ +/* Set the text content of an attribute. */ +/******************************************************************/ +bool DOMNODE::SetContent(PGLOBAL g, char *txtp, int len) + { + bool rc; + BSTR val; + + if (len > Len || !Ws) { + Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2); + Len = len; + } // endif len + + if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1, + Ws, Len + 1)) { + sprintf(g->Message, MSG(WS_CONV_ERR), txtp); + return true; + } // endif + + val = SysAllocString(Ws); + rc = TestHr(g, Nodep->put_text(val)); + SysFreeString(val); + return rc; + } // end of SetContent + +/******************************************************************/ +/* Return a clone of this node. */ +/******************************************************************/ +PXNODE DOMNODE::Clone(PGLOBAL g, PXNODE np) + { + if (np) { + ((PDOMNODE)np)->Nodep = Nodep; + return np; + } else + return new(g) DOMNODE(Doc, Nodep); + + } // end of Clone + +/******************************************************************/ +/* Return the list of all or matching children that are elements.*/ +/******************************************************************/ +PXLIST DOMNODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp) + { + MSXML2::IXMLDOMNodeListPtr dnlp; + + if (xp) { + if (Nodep->nodeType == MSXML2::NODE_ELEMENT) { + MSXML2::IXMLDOMElementPtr ep = Nodep; + dnlp = ep->getElementsByTagName(xp); + } else + return NULL; + + } else + dnlp = Nodep->childNodes; + + if (lp) { + ((PDOMLIST)lp)->Listp = dnlp; + return lp; + } else + return new(g) DOMNODELIST(Doc, dnlp); + + } // end of GetChildElements + +/******************************************************************/ +/* Return the list of nodes verifying the passed Xapth. */ +/******************************************************************/ +PXLIST DOMNODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) + { + MSXML2::IXMLDOMNodeListPtr dnlp = Nodep->selectNodes(xp); + + if (lp) { + ((PDOMLIST)lp)->Listp = dnlp; + return lp; + } else + return new(g) DOMNODELIST(Doc, dnlp); + + } // end of SelectNodes + +/******************************************************************/ +/* Return the first node verifying the passed Xapth. */ +/******************************************************************/ +PXNODE DOMNODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) + { + MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp); + + if (dnp) { + if (np) { + ((PDOMNODE)np)->Nodep = dnp; + return np; + } else + return new(g) DOMNODE(Doc, dnp); + + } else + return NULL; + + } // end of SelectSingleNode + +/******************************************************************/ +/* Return the node attribute with the specified name. */ +/******************************************************************/ +PXATTR DOMNODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap) + { + MSXML2::IXMLDOMElementPtr ep = Nodep; + MSXML2::IXMLDOMAttributePtr atp = ep->getAttributeNode(name); + + if (atp) { + if (ap) { + ((PDOMATTR)ap)->Atrp = atp; + return ap; + } else + return new(g) DOMATTR(Doc, atp); + + } else + return NULL; + + } // end of GetAttribute + +/******************************************************************/ +/* Add a new element child node to this node and return it. */ +/******************************************************************/ +PXNODE DOMNODE::AddChildNode(PGLOBAL g, char *name, PXNODE np) + { + char *p, *pn; +// char *p, *pn, *epf, *pf = NULL; + MSXML2::IXMLDOMNodePtr ep; +// _bstr_t uri((wchar_t*)NULL); + +#if 0 + // Is a prefix specified ? + if ((p = strchr(name, ':'))) { + pf = BufAlloc(g, name, p - name); + + // Is it the pseudo default prefix + if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) { + name = p + 1; // Suppress it from name + pf = NULL; // No real prefix + } // endif DefNs + + } // endif p + + // Look for matching namespace URI in context + for (ep = Nodep; ep; ep = ep->parentNode) { + epf = (_bstr_t)ep->prefix; + + if ((!pf && !epf) || (pf && epf && !strcmp(pf, epf))) { + uri = Nodep->namespaceURI; + break; + } // endif + + } // endfor ep + + if ((wchar_t*)uri == NULL) { + if (!pf) + pf = Doc->DefNs; + + // Look for the namespace URI corresponding to this node + if (pf) + for (PNS nsp = Doc->Namespaces; nsp; nsp = nsp->Next) + if (!strcmp(pf, nsp->Prefix)) { + uri = nsp->Uri; + break; + } // endfor nsp + + } // endif pns +#endif // 0 + + // If name has the format m[n] only m is taken as node name + if ((p = strchr(name, '['))) + pn = BufAlloc(g, name, p - name); + else + pn = name; + + // Construct the element node with eventual namespace +// ep = Docp->createNode(_variant_t("Element"), pn, uri); + ep = Docp->createElement(pn); + + _bstr_t pfx = ep->prefix; + _bstr_t uri = ep->namespaceURI; + + if (ep == NULL || TestHr(g, Nodep->appendChild(ep))) + return NULL; + + if (np) + ((PDOMNODE)np)->Nodep = ep; + else + np = new(g) DOMNODE(Doc, ep); + + return NewChild(np); + } // end of AddChildNode + +/******************************************************************/ +/* Add a new property to this node and return it. */ +/******************************************************************/ +PXATTR DOMNODE::AddProperty(PGLOBAL g, char *name, PXATTR ap) + { + MSXML2::IXMLDOMAttributePtr atp = Docp->createAttribute(name); + + if (atp) { + MSXML2::IXMLDOMElementPtr ep = Nodep; + ep->setAttributeNode(atp); + + if (ap) { + ((PDOMATTR)ap)->Atrp = atp; + return ap; + } else + return new(g) DOMATTR(Doc, atp); + + } else + return NULL; + + } // end of AddProperty + +/******************************************************************/ +/* Add a new text node to this node. */ +/******************************************************************/ +void DOMNODE::AddText(PGLOBAL g, char *txtp) + { + MSXML2::IXMLDOMTextPtr tp= Docp->createTextNode((_bstr_t)txtp); + + if (tp != NULL) + TestHr(g, Nodep->appendChild(tp)); + + } // end of AddText + +/******************************************************************/ +/* Remove a child node from this node. */ +/******************************************************************/ +void DOMNODE::DeleteChild(PGLOBAL g, PXNODE dnp) + { + TestHr(g, Nodep->removeChild(((PDOMNODE)dnp)->Nodep)); +// ((PDOMNODE)dnp)->Nodep->Release(); bad idea, causes a crash + Delete(dnp); + } // end of DeleteChild + +/* --------------------- class DOMNODELIST ---------------------- */ + +/******************************************************************/ +/* DOMNODELIST constructor. */ +/******************************************************************/ +DOMNODELIST::DOMNODELIST(PXDOC dp, MSXML2::IXMLDOMNodeListPtr lp) + : XMLNODELIST(dp) + { + Listp = lp; + } // end of DOMNODELIST constructor + +/******************************************************************/ +/* Return the nth element of the list. */ +/******************************************************************/ +PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np) + { + if (Listp == NULL || Listp->length <= n) + return NULL; + + if (np) { + ((PDOMNODE)np)->Nodep = Listp->item[n]; + return np; + } else + return new(g) DOMNODE(Doc, Listp->item[n]); + + } // end of GetItem + +/* ----------------------- class DOMATTR ------------------------ */ + +/******************************************************************/ +/* DOMATTR constructor. */ +/******************************************************************/ +DOMATTR::DOMATTR(PXDOC dp, MSXML2::IXMLDOMAttributePtr ap) + : XMLATTRIBUTE(dp) + { + Atrp = ap; + Ws = NULL; + Len = 0; + } // end of DOMATTR constructor + +/******************************************************************/ +/* Set the text content of an attribute. */ +/******************************************************************/ +bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len) + { + bool rc; + BSTR val; + + if (len > Len || !Ws) { + Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2); + Len = len; + } // endif len + + if (!MultiByteToWideChar(CP_ACP, 0, txtp, strlen(txtp) + 1, + Ws, Len + 1)) { + sprintf(g->Message, MSG(WS_CONV_ERR), txtp); + return true; + } // endif + + val = SysAllocString(Ws); + rc = TestHr(g, Atrp->put_text(val)); + SysFreeString(val); + return rc; + } // end of SetText diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h new file mode 100644 index 00000000000..fc157950df8 --- /dev/null +++ b/storage/connect/domdoc.h @@ -0,0 +1,138 @@ +/******************************************************************/ +/* Declaration of XML document processing using MS DOM */ +/* Author: Olivier Bertrand 2007 - 2012 */ +/******************************************************************/ +#include "plgxml.h" + +typedef class DOMDOC *PDOMDOC; +typedef class DOMNODE *PDOMNODE; +typedef class DOMATTR *PDOMATTR; +typedef class DOMNODELIST *PDOMLIST; + +/******************************************************************/ +/* XML block. Must have the same layout than FBLOCK up to Type. */ +/******************************************************************/ +typedef struct _xblock { /* Loaded XML file block */ + struct _xblock *Next; + LPCSTR Fname; /* Point on file name */ + size_t Length; /* Used to tell if read mode */ + short Count; /* Nb of times file is used */ + short Type; /* TYPE_FB_XML */ + int Retcode; /* Return code from Load */ + MSXML2::IXMLDOMDocumentPtr Docp;/* Document interface pointer */ +//IXMLDOMNodeListPtr Nlist; + } XBLOCK, *PXBLOCK; + +/******************************************************************/ +/* Declaration of DOM document. */ +/******************************************************************/ +class DOMDOC : public XMLDOCUMENT { + friend class DOMNODE; + public: + // Constructor + DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp); + + // Properties + virtual short GetDocType(void) {return TYPE_FB_XML;} + virtual void *GetDocPtr(void) {return Docp;} + + // Methods + virtual bool Initialize(PGLOBAL g); + virtual bool ParseFile(char *fn); + virtual bool NewDoc(PGLOBAL g, char *ver); + virtual void AddComment(PGLOBAL g, char *com); + virtual PXNODE GetRoot(PGLOBAL g); + virtual PXNODE NewRoot(PGLOBAL g, char *name); + virtual PXNODE NewPnode(PGLOBAL g, char *name); + virtual PXATTR NewPattr(PGLOBAL g); + virtual PXLIST NewPlist(PGLOBAL g); + virtual int DumpDoc(PGLOBAL g, char *ofn); + virtual void CloseDoc(PGLOBAL g, PFBLOCK xp); + virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn); + + protected: + // Members + MSXML2::IXMLDOMDocumentPtr Docp; + MSXML2::IXMLDOMNodeListPtr Nlist; + HRESULT Hr; +}; // end of class DOMDOC + +/******************************************************************/ +/* Declaration of DOM XML node. */ +/******************************************************************/ +class DOMNODE : public XMLNODE { + friend class DOMDOC; + friend class DOMNODELIST; + public: + // Properties + virtual char *GetName(PGLOBAL g); + virtual int GetType(void) {return Nodep->nodeType;} + virtual PXNODE GetNext(PGLOBAL g); + virtual PXNODE GetChild(PGLOBAL g); + + // Methods + virtual RCODE GetContent(PGLOBAL g, char *buf, int len); + virtual bool SetContent(PGLOBAL g, char *txtp, int len); + virtual PXNODE Clone(PGLOBAL g, PXNODE np); + virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp); + virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp); + virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np); + virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap); + virtual PXNODE AddChildNode(PGLOBAL g, char *name, PXNODE np); + virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap); + virtual void AddText(PGLOBAL g, char *txtp); + virtual void DeleteChild(PGLOBAL g, PXNODE dnp); + + protected: + // Constructor + DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np); + + // Members + MSXML2::IXMLDOMDocumentPtr Docp; + MSXML2::IXMLDOMNodePtr Nodep; + char Name[64]; + WCHAR *Ws; + int Len; +}; // end of class DOMNODE + +/******************************************************************/ +/* Declaration of DOM XML node list. */ +/******************************************************************/ +class DOMNODELIST : public XMLNODELIST { + friend class DOMDOC; + friend class DOMNODE; + public: + // Methods + virtual int GetLength(void) {return Listp->length;} + virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); + + protected: + // Constructor + DOMNODELIST(PXDOC dp, MSXML2::IXMLDOMNodeListPtr lp); + + // Members + MSXML2::IXMLDOMNodeListPtr Listp; +}; // end of class DOMNODELIST + +/******************************************************************/ +/* Declaration of DOM XML attribute. */ +/******************************************************************/ +class DOMATTR : public XMLATTRIBUTE { + friend class DOMDOC; + friend class DOMNODE; + public: + // Properties +//virtual char *GetText(void); + + // Methods + virtual bool SetText(PGLOBAL g, char *txtp, int len); + + protected: + // Constructor + DOMATTR(PXDOC dp, MSXML2::IXMLDOMAttributePtr ap); + + // Members + MSXML2::IXMLDOMAttributePtr Atrp; + WCHAR *Ws; + int Len; +}; // end of class DOMATTR diff --git a/storage/connect/engmsg.h b/storage/connect/engmsg.h new file mode 100644 index 00000000000..ccced92261e --- /dev/null +++ b/storage/connect/engmsg.h @@ -0,0 +1,1013 @@ +#define MSG_ACCESS_VIOLATN "Access violation" +#define MSG_ACT_ALLOC_FAIL "PlugInitLang: Activity allocation failed" +#define MSG_ADDVAL_ERROR "Error %d in AddValue" +#define MSG_ADD_BAD_TYPE "Array add value type mismatch (%s -> %s)" +#define MSG_ADD_NULL_DOM "Adding string %s to a null domain" +#define MSG_ADPOS_IN_DICTP "ADPOS to work in User_Dictp" +#define MSG_AFTER " after: " +#define MSG_ALG_CHOICE_AUTO "Best algorithm choice is automatic" +#define MSG_ALG_CHOICE_BAD "Bad algorithm choice value, reset to AUTO" +#define MSG_ALG_CHOICE_QRY "Using Query algorithm" +#define MSG_ALG_CURLY_BRK "Algorithm choice depends on outer curly brackets" +#define MSG_ALLOC_ERROR "Error allocating %s" +#define MSG_ALL_DELETED "All lines deleted in %.2lf sec" +#define MSG_ALTER_DB_ERR "Cannot find the DB to alter" +#define MSG_AMBIG_COL_QUAL "Ambiguous qualifier %s for column %s" +#define MSG_AMBIG_CORREL "Select %s.* refers more than one table" +#define MSG_AMBIG_SPEC_COL "Ambiguous special column %s" +#define MSG_ANSWER_TYPE "Answer of type" +#define MSG_API_CONF_ERROR "SQL Error: API_CONFORMANCE" +#define MSG_APPL_ACCESSIBLE "Application %s accessible" +#define MSG_APPL_ACTIVE "Application %s still active" +#define MSG_APPL_BAD_SAVE "Application %s may be incorrectly saved" +#define MSG_APPL_CREATED "Application %s created" +#define MSG_APPL_IS_ACTIVE "Application already active" +#define MSG_APPL_NOT_INIT "Application not initialized" +#define MSG_APPL_NOT_LOADED "Application not loaded" +#define MSG_APPL_QUIT "Application %s quit" +#define MSG_APPL_SAVED "Application %s saved" +#define MSG_APP_STILL_ACTIV "Application of language %s still active (not freed)" +#define MSG_AREAFILE_NOTFND "Area file not found" +#define MSG_ARGS_SYNTAX_ERR "?SetArgs syntax error: unexpected %s after %s" +#define MSG_ARG_ALREADY_SET "Argument %d already set" +#define MSG_ARG_NOT_AN_ATTR "Argument is not an attribute (wrong pos-type %d)" +#define MSG_ARG_OUT_CONTEXT "@-type argument used out of context" +#define MSG_ARG_OUT_RANGE "Phrase argument of value %d is out of range" +#define MSG_ARG_PTR_NOSEM "Argument of value %d points to a nonterm having no Sem" +#define MSG_ARG_PTR_NOSEMS "Argument of value %d points to a nonterm having no semantics" +#define MSG_ARG_REF_LOOP "?Looping argument cross references" +#define MSG_ARG_TWO_CONST "2nd argument of %s must be a constant" +#define MSG_ARRAY_ALLOC_ERR "Memory allocation error in ARRAY" +#define MSG_ARRAY_BNDS_EXCD "Array bounds exceeded" +#define MSG_ARRAY_ERROR "Error while making array k=%d n=%d" +#define MSG_ATTRIBUTE_ERROR "Error rule %u attribute %s: " +#define MSG_ATT_NOT_CASE "Attribute has wrong value %d (not a casevalue)" +#define MSG_ATT_POSCODE_BIG "Attribute poscode %d is too big (max=%d)" +#define MSG_AVGLEN_ERROR "avglen should be between %d and %d" +#define MSG_BAD_AGGREG_FUNC "Unsupported aggregate function %d" +#define MSG_BAD_ARGTYPES "Argument type invalid for %s" +#define MSG_BAD_ARGUMENTS "Argument not attached for %s" +#define MSG_BAD_ARG_NUM "Invalid number of arguments %d" +#define MSG_BAD_ARG_TYPE "Bad argument type %d" +#define MSG_BAD_ARRAY_OPER "Arrays must be used with the IN operator" +#define MSG_BAD_ARRAY_TYPE "Illegal array type %d" +#define MSG_BAD_ARRAY_VAL "Arrays must have the same number of values" +#define MSG_BAD_BIN_FMT "Invalid format %c for the %s BIN column" +#define MSG_BAD_BLK_ESTIM "Number of blocks exceeds estimate" +#define MSG_BAD_BLK_SIZE "No match in block %d size" +#define MSG_BAD_BYTE_NUM "bad number of bytes written" +#define MSG_BAD_BYTE_READ "bad number of bytes read" +#define MSG_BAD_CARDINALITY "Invalid Cardinality call for multiple table" +#define MSG_BAD_CASE_SPEC "Wrong case specification %c, enter new one: " +#define MSG_BAD_CHAR_SPEC "Invalid character specification:'%s'" +#define MSG_BAD_CHECK_TYPE "Invalid CheckColumn subtype %d" +#define MSG_BAD_CHECK_VAL "Bad check setting '%s'" +#define MSG_BAD_COLCRT_ARG "Bad COLCRT argument (type=%hd, domain=%hd)" +#define MSG_BAD_COLDEF_TYPE "Coldefs: wrong type %d" +#define MSG_BAD_COLIST_ITEM "Incorrect colist item" +#define MSG_BAD_COLIST_TYPE "Bad Colist type=%d" +#define MSG_BAD_COLSIZE "Colsize %d is too small for this database" +#define MSG_BAD_COL_ENTRY "Invalid entry for column %s" +#define MSG_BAD_COL_FORMAT "Invalid column format type %d" +#define MSG_BAD_COL_IN_FILT "Incorrect column in filter" +#define MSG_BAD_COL_QUALIF "Bad qualifier %s for column %s" +#define MSG_BAD_COL_TYPE "Invalid type %s for column %s" +#define MSG_BAD_COL_XPATH "Invalid Xpath in column %s for HTML table %s" +#define MSG_BAD_COMPARE_OP "Bad compare op %d" +#define MSG_BAD_CONST_TYPE "Bad constant type=%d" +#define MSG_BAD_CONV_TYPE "Invalid convert type %d" +#define MSG_BAD_CORREL "Select %s.* correlation refers no tables" +#define MSG_BAD_DATETIME "Invalid datetime value" +#define MSG_BAD_DATE_OPER "Unexpected date operator %d" +#define MSG_BAD_DBF_FILE "DBF file %s is corrupted" +#define MSG_BAD_DBF_REC "DBF file %s corrupted at record %d" +#define MSG_BAD_DBF_TYPE "Unsupported DBF type %c" +#define MSG_BAD_DEF_ARG "Bad INDEXDEF argument (type=%hd, domain=%hd)" +#define MSG_BAD_DEF_READ "Unexpected EOF in deferred Read" +#define MSG_BAD_DEF_TYPE "Invalid column definition type" +#define MSG_BAD_DIRECTORY "Bad directory %s: %s" +#define MSG_BAD_DIST_JN_FIL "Invalid Distinct Join filter" +#define MSG_BAD_DIST_JOIN "Invalid Distinct Join specification" +#define MSG_BAD_DOM_COL_DEF "Invalid column definitions for a domain" +#define MSG_BAD_DOM_VALUE "%d is not a valid domain value" +#define MSG_BAD_EDIT_INIT "Coparm: edition %s not properly initialized" +#define MSG_BAD_EVAL_TYPE "Bad scalar function eval type=%d" +#define MSG_BAD_EXEC_MODE "Bad execution mode '%s'" +#define MSG_BAD_EXP_ARGTYPE "Invalid expression argument type=%d" +#define MSG_BAD_EXP_OPER "Bad expression operator=%d" +#define MSG_BAD_FETCH_RC "Unexpected Fetch return code %d" +#define MSG_BAD_FIELD_FMT "Invalid field format %c for %s" +#define MSG_BAD_FIELD_RANK "Invalid field rank %d for column %s" +#define MSG_BAD_FIELD_TYPE "Bad type field %s" +#define MSG_BAD_FILE_HANDLE "Invalid File Handle: %s" +#define MSG_BAD_FILE_LIST "Bad Filelist section" +#define MSG_BAD_FILTER "Bad filter: Opc=%d B_T=%d %d Type=%d %d" +#define MSG_BAD_FILTER_CONV "Bad filter conversion, B_T=%d,%d" +#define MSG_BAD_FILTER_LINK "Bad filter link operator %d" +#define MSG_BAD_FILTER_OP "Invalid filter operator %d" +#define MSG_BAD_FILTEST_OP "Invalid operator %d %d for FilTest" +#define MSG_BAD_FLD_FORMAT "Bad format for field %d of %s" +#define MSG_BAD_FLD_LENGTH "Field %s too long (%s --> %d) line %d of %s" +#define MSG_BAD_FLOAT_CONV "Invalid convert of float array" +#define MSG_BAD_FPARM_NEXT "Coparm: FPARM with non-null Next" +#define MSG_BAD_FREQ_SET "Bad frequency setting for column %s" +#define MSG_BAD_FUNC_ARG "Funcarg of type %d not implemented" +#define MSG_BAD_FUNC_ARGTYP "Bad Function argument type=%d" +#define MSG_BAD_FUNC_MODE "%s: invalid mode %d" +#define MSG_BAD_GENRE "Genre is invalid" +#define MSG_BAD_GETVIEW_RET "GetView: invalid return type %d" +#define MSG_BAD_HANDLE_VAL "Invalid handle value" +#define MSG_BAD_HAV_FILTER "Having filter found in a Vanilla query" +#define MSG_BAD_HAV_FILTYPE "Bad filter type for Having Clause" +#define MSG_BAD_HEADER "File %s: Header corrupted" +#define MSG_BAD_HEADER_VAL "Invalid header value %d" +#define MSG_BAD_HEAD_END "Can't read end of header" +#define MSG_BAD_INDEX_COL "Bad column %s for index %s" +#define MSG_BAD_INDEX_DEF "Bad index definition for %s" +#define MSG_BAD_INDEX_FILE "Wrong index file %s" +#define MSG_BAD_INDEX_PART "Bad index part for %s" +#define MSG_BAD_INPUT "Incorrect input" +#define MSG_BAD_IN_ARGTYPE "Bad argument type for IN operator" +#define MSG_BAD_IN_ENDING "Error: wrong end of IN string" +#define MSG_BAD_IN_STRING "IN string begins or ends with invalid char %c ... %c" +#define MSG_BAD_JCOL_TYPE "Logical JCT error: Unmatched column types" +#define MSG_BAD_JOIN_EXP "Invalid expression used in join" +#define MSG_BAD_JOIN_FILTER "Improper join filter" +#define MSG_BAD_JOIN_OP "Bad join operator %d" +#define MSG_BAD_LANG_SIZE "Wrong Language file size %d" +#define MSG_BAD_LINEFLD_FMT "Bad format line %d field %d of %s" +#define MSG_BAD_LINE_LEN "Line length not equal to Lrecl" +#define MSG_BAD_LIST_TYPE "Invalid list type=%d" +#define MSG_BAD_LOCALE "Invalid locale %s" +#define MSG_BAD_LOCDFON_ARG "Bad parameter for LOCDFON" +#define MSG_BAD_LOCNODE_USE "Unexpected use of LOCNODE" +#define MSG_BAD_LRECL "Table/File lrecl mismatch (%d,%hd)" +#define MSG_BAD_MAX_HAVING "MAXTMP value too small for Having" +#define MSG_BAD_MAX_NREC "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d" +#define MSG_BAD_MAX_PARAM "Bad parameters for setting max value" +#define MSG_BAD_MAX_SETTING "Bad max setting '%c'" +#define MSG_BAD_MERGE_TYPE "Type %d cannot be merged" +#define MSG_BAD_NODE_TYPE "Bad type %d for table node" +#define MSG_BAD_OFFSET_VAL "Invalid null offset value for a CSV table" +#define MSG_BAD_OPEN_MODE "Invalid open mode %d" +#define MSG_BAD_OPERATOR "Invalid operator %s" +#define MSG_BAD_ORDER_MODE "Bad ordering mode %c" +#define MSG_BAD_ORDER_TYPE "Type=%d invalid for order item" +#define MSG_BAD_OUTER_JOIN "Invalid outer join on child table" +#define MSG_BAD_PAD_ARGTYP "Bad argument type for Pad or Justify" +#define MSG_BAD_PARAMETERS "%.8s: Bad parameters" +#define MSG_BAD_PARAM_TYPE "%.8s: Bad parameter type=%d" +#define MSG_BAD_PARM_COUNT "Parameter count mismatch" +#define MSG_BAD_PHASE_NUM "Out of range phrase number %d" +#define MSG_BAD_PHRASE_NB "out of range phrase number %d rc=%d\n" +#define MSG_BAD_POS_CODE "Invalid POS code %d" +#define MSG_BAD_POS_TYPE "Invalid POS code type %d" +#define MSG_BAD_PROJNUM "Bad projnum %d for column %s" +#define MSG_BAD_QUERY_OPEN "Query open invalid mode %d" +#define MSG_BAD_QUERY_TYPE "Invalid query type %d for %s" +#define MSG_BAD_QUOTE_FIELD "Missing ending quote in %s field %d line %d" +#define MSG_BAD_READ_NUMBER "Wrong number %d of values read from %s" +#define MSG_BAD_RECFM "Invalid recfm type %d for DOSCOL" +#define MSG_BAD_RECFM_VAL "Bad Recfm value %d" +#define MSG_BAD_RESULT_TYPE "Bad result type %d for %s" +#define MSG_BAD_RETURN_TYPE "Bad returned type %d" +#define MSG_BAD_ROW_VALIST "Invalid ROW list of values" +#define MSG_BAD_ROW_VALNB "Number of values in list mismatch" +#define MSG_BAD_SCF_ARGTYPE "Argument %d type=%s invalid for %s" +#define MSG_BAD_SEM_DOMAIN "Invalid domain .%d" +#define MSG_BAD_SETTINGS "Some settings do not match table type" +#define MSG_BAD_SET_CASE "Cannot set sensitive an insensitive array" +#define MSG_BAD_SET_STRING "Invalid SetValue from string" +#define MSG_BAD_SET_TYPE "Bad set type %hd" +#define MSG_BAD_SPECIAL_CMD "Ill formed special command" +#define MSG_BAD_SPECIAL_COL "Bad special column %s" +#define MSG_BAD_SPEC_COLUMN "Special column invalid for this table type" +#define MSG_BAD_SQL_PARAM "Invalid SQL parameter for FindColblk" +#define MSG_BAD_SUBLST_TYPE "Coparm: bad sub-list type %d" +#define MSG_BAD_SUBSEL_IN_X "Bad sub-select in expression" +#define MSG_BAD_SUBSEL_TYPE "Bad Sub-Select returned type %d" +#define MSG_BAD_SUB_RESULT "Undefined Sub-Select function result" +#define MSG_BAD_SUB_SELECT "Bad sub-select in function argument" +#define MSG_BAD_TABLE_LINE "Illegal or truncated line '%s' in Tables section" +#define MSG_BAD_TABLE_LIST "Table %s not found in table list" +#define MSG_BAD_TABLE_TYPE "Bad type %s for table %s" +#define MSG_BAD_TEST_TYPE "Array BlockTest type mismatch %s %s" +#define MSG_BAD_TRIM_ARGTYP "Bad argument type for Trim" +#define MSG_BAD_TYPE_FOR_IN "Arg type mismatch for IN function" +#define MSG_BAD_TYPE_FOR_S "Incorrect type %d for %s(%d)" +#define MSG_BAD_TYPE_LIKE "Bad operand(%d) type=%d for LIKE" +#define MSG_BAD_UPD_COR "Qualifier %s of column %s not related to the updated table %s" +#define MSG_BAD_USERBLK_LEN "User block write length error" +#define MSG_BAD_USETEMP "Bad usetemp setting '%s'" +#define MSG_BAD_USETEMP_VAL "Bad Usetemp value %d" +#define MSG_BAD_VALBLK_INDX "Out of range valblock index value" +#define MSG_BAD_VALBLK_TYPE "Invalid value block type %d" +#define MSG_BAD_VALNODE "Bad type %d for column %s value node" +#define MSG_BAD_VALUE_TYPE "Invalid value type %d" +#define MSG_BAD_VAL_UPDATE "Don't know which %s value to update" +#define MSG_BAD_VIEW_OPEN "View open invalid mode %d" +#define MSG_BAD_XMODE_VAL "Bad execution mode %d" +#define MSG_BAD_XOBJ_TYPE "Bad Xobject type %d" +#define MSG_BAS_NS_LIST "Invalid namespaces list format" +#define MSG_BIN_F_TOO_LONG "Value too long for field %s (%d --> %d)" +#define MSG_BIN_MODE_FAIL "Set binary mode failed: %s" +#define MSG_BLKTYPLEN_MISM "Non matching block types/lengths in SetValue" +#define MSG_BLK_IS_NULL "Blk is NULL" +#define MSG_BLOCK_NO_MATCH "Non matching block" +#define MSG_BREAKPOINT "Breakpoint" +#define MSG_BUFF_TOO_SMALL "GetColData: Buffer is too small" +#define MSG_BUFSIZE_ERROR "Error getting screen buffer size" +#define MSG_BUILDING_GROUPS "Building groups" +#define MSG_BUILD_DIST_GRPS "Building groups distinct" +#define MSG_BUILD_INDEX "Building index %s on %s" +#define MSG_BXP_NULL "Bxp NULL in PUTFON" +#define MSG_CANNOT_OPEN "Cannot open %s" +#define MSG_CD_ONE_STEP "Count Distinct must be processed in one step" +#define MSG_CD_ORDER_ERROR "Ordering error in Count Distinct" +#define MSG_CHECKING_ROWS "Checking rows to update" +#define MSG_CHECK_LEVEL "Checking level reset to %u" +#define MSG_CHSIZE_ERROR "chsize error: %s" +#define MSG_CLN_NOT_IN_JOIN "Column C%d not found in join" +#define MSG_CNTDIS_COL_LOST "Count Distinct column lost" +#define MSG_COLIST_BAD_TYPE "Invalid Colist element type=%d" +#define MSG_COLNAM_TOO_LONG "Column name too long" +#define MSG_COLSEC_TOO_BIG "Column section too big in table %s (%d)" +#define MSG_COLS_REDUCED " (reduced by Maxcol)" +#define MSG_COLUMN_ERROR "Column error" +#define MSG_COLUMN_MISMATCH "Column %s mismatch" +#define MSG_COLUMN_NOT_KEY "Join column R%d.%s is not a key" +#define MSG_COL_ALLOC_ERR "Cannot allocate column node" +#define MSG_COL_ALLOC_ERROR "Memory allocation error for column %d" +#define MSG_COL_HAS_NO_DEF "Column %s has no definition" +#define MSG_COL_INVAL_TABLE "Column %s.%s not found in table %s alias %s" +#define MSG_COL_ISNOT_TABLE "Column %s is not in table %s" +#define MSG_COL_NB_MISM "Number of columns mismatch" +#define MSG_COL_NOTIN_GRPBY "Column %s not in Group By list" +#define MSG_COL_NOTIN_TABLE "Column %s is not in any table" +#define MSG_COL_NOTIN_UPDT "%s does not belong to the updated table %s" +#define MSG_COL_NOT_CODED "Column %s is not coded" +#define MSG_COL_NOT_EXIST "Column %s is not in table %s" +#define MSG_COL_NOT_FOUND "Column %s does not exist in %s" +#define MSG_COL_NOT_IN_DB "Column %s of table %s not in DB" +#define MSG_COL_NOT_IN_JOIN "Column %s not found in join" +#define MSG_COL_NOT_SORTED "Column %s of table %s is not sorted" +#define MSG_COL_NUM_MISM "Number of columns mismatch" +#define MSG_COL_USED_TWICE "Column %s linked twice ???" +#define MSG_COMPUTE_ERROR "Error in Compute, op=%d" +#define MSG_COMPUTE_NIY "Compute not implemented for token strings" +#define MSG_COMPUTING "Computing" +#define MSG_COMPUTING_DIST "Computing Distinct" +#define MSG_COMPUTING_FUNC "Computing function(s)" +#define MSG_COM_ERROR "Com error" +#define MSG_CONCAT_SUBNODE "Cannot concatenate sub-nodes" +#define MSG_CONNECTED "Connected" +#define MSG_CONNECT_CANCEL "Connection cancelled by user" +#define MSG_CONNECT_ERROR "Error %d connecting to %s" +#define MSG_CONN_CLOSED "%s(%d) closed" +#define MSG_CONN_CREATED "Connection %s created" +#define MSG_CONN_DROPPED "Connection %s dropped" +#define MSG_CONN_OPEN "%s(%d) open (%s)" +#define MSG_CONN_SUC_OPEN "%s(%d) successfully open" +#define MSG_CONTROL_C_EXIT "Control C exit" +#define MSG_COPY_BAD_PHASE "List copy invalid in phase %d" +#define MSG_COPY_INV_TYPE "Coparm: type not supported %d" +#define MSG_CORREL_NO_QRY "Correlated subqueries cannot be of QRY type" +#define MSG_CREATED_PLUGDB " Created by PlugDB %s " +#define MSG_CURSOR_SET "Cursor set to %d" +#define MSG_DATABASE_ACTIVE "Database %s activated" +#define MSG_DATABASE_LOADED "Database %s loaded" +#define MSG_DATA_IS_NULL "ExecSpecialCmd: data is NULL" +#define MSG_DATA_MISALIGN "Datatype misalignment" +#define MSG_DBASE_FILE "dBASE dbf file: " +#define MSG_DB_ALREADY_DEF "Database %s already defined" +#define MSG_DB_ALTERED "Database altered" +#define MSG_DB_CREATED "Database %s created" +#define MSG_DB_NOT_SPEC "Database not specified" +#define MSG_DB_REMOVED "Database %s removed from DB list" +#define MSG_DB_SORT_ERROR "Error in DB sort" +#define MSG_DB_STOPPED "Database %s stopped" +#define MSG_DEBUG_NOT_ACTIV "Debug is not active" +#define MSG_DEBUG_SET_INV "Invalid Debug set %c" +#define MSG_DEF_ALLOC_ERROR "Error allocating %s DEF class" +#define MSG_DELETING_ROWS "Deleting rows" +#define MSG_DEL_FILE_ERR "Error deleting %s" +#define MSG_DEL_READ_ERROR "Delete: read error req=%d len=%d" +#define MSG_DEL_WRITE_ERROR "Delete: write error: %s" +#define MSG_DEPREC_FLAG "Deprecated option Flag, use Coltype" +#define MSG_DICTIONARY "Dictionary " +#define MSG_DIRECT_VARTOK "Direct access of variable token rules not implemented" +#define MSG_DISCONNECTED "Disonnected" +#define MSG_DISTINCT_ERROR "More than one DISTINCT functional item" +#define MSG_DISTINCT_ROWS "Selecting distinct rows" +#define MSG_DISTINCT_VALUES "Retrieving distinct values" +#define MSG_DIS_NOHEAD_JOIN "Distinct join on not heading table" +#define MSG_DLL_LOAD_ERROR "Error %d loading module %s" +#define MSG_DOMAIN_EMPTY "Domain %s is empty" +#define MSG_DOMAIN_ERROR "Column %s domain(%s)/value(%s) mismatch" +#define MSG_DOMAIN_FULL "Domain %s is full (max=%d)" +#define MSG_DOM_FILE_ERROR "Domain file %s not found" +#define MSG_DOM_NOT_SUPP "MS-DOM not supported by this version" +#define MSG_DOM_OPEN_ERROR "Domain open error: %s" +#define MSG_DOM_READ_ERROR "Domain read error %d: %s" +#define MSG_DOM_READ_ONLY "Domain table %s is read only" +#define MSG_DOM_WRITE_ERROR "Domain write error %d: %s" +#define MSG_DONE "Done, rc=%d" +#define MSG_DOSALMEM_NOMEM "Memory Allocation failed, not enough memory" +#define MSG_DROP_DB_ERR "Drop database %s failed" +#define MSG_DSORT_LOG_ERROR "Logical error in Kindex distinct Sort" +#define MSG_DUMMY_NO_COLS "Dummy tables must have no columns" +#define MSG_DUPLICAT_COUNT "Count on more than one column" +#define MSG_DUP_COL_NAME "Duplicate column name %s" +#define MSG_DUP_PROJNUM "Duplicated projnum %d for column %s" +#define MSG_DVAL_NOTIN_LIST "Value %s not found in distinct values list of column %s" +#define MSG_EMPTY_DOC "Empty document" +#define MSG_EMPTY_FILE "%s empty file %s: " +#define MSG_ENDSTR_MISMATCH "No match between end of string and end of node" +#define MSG_END_OF_DELETE "%d line(s) deleted in %.2lf sec" +#define MSG_END_OF_INSERT "%d line(s) inserted in %.2lf sec" +#define MSG_END_OF_QUERY "%d line(s) retrieved in %.2lf sec" +#define MSG_END_OF_UPDATE "%d line(s) updated in %.2lf sec" +#define MSG_EOF_AFTER_LINE "EOF after line %d" +#define MSG_EOF_INDEX_FILE "EOF while reading index file" +#define MSG_ERASED " and erased" +#define MSG_ERASE_FAILED " (erase failed)" +#define MSG_ERROR "Error" +#define MSG_ERROR_IN_LSK "Error %d in lseek64" +#define MSG_ERROR_IN_SFP "Error %d in SetFilePointer" +#define MSG_ERROR_NO_PARM "No parameter (valid only with %.8s.1 and %.8s.5)" +#define MSG_ERROR_OPENING "Error opening: " +#define MSG_ERR_NUM_GT_MAX "Error: Numval (%d) greater than Maxnum (%d)" +#define MSG_ERR_READING_REC "Error reading record %d of %s" +#define MSG_ERR_RET_RULE "error return, rule=%u" +#define MSG_ERR_RET_TYPE "error return, type=%d" +#define MSG_EVAL_EXPIRED "Evaluation version expired" +#define MSG_EVAL_ONLY "I agree to use this Dll for evaluation purpose only" +#define MSG_EXECUTING "Executing" +#define MSG_EXECUTION_ERROR "Execution error" +#define MSG_EXEC_MODE_IS "Execution mode is %s" +#define MSG_EXEC_MODE_RESET ". Mode reset to Execute" +#define MSG_EXEC_MODE_SET "Execution mode set to %s" +#define MSG_EXIT_EVAL_ERR "Error evaluating Exit" +#define MSG_EXIT_FROM_LANG "Exit from language %s version %d.%d" +#define MSG_FAIL_ADD_NODE "Failed to add %s table node" +#define MSG_FETCHING_DATA "Fetching data" +#define MSG_FETCHING_ROWS "Fetching rows" +#define MSG_FETCH_NO_RES "Fetch: No Result Set" +#define MSG_FIELD_TOO_LONG "Value too long for field %d line %d" +#define MSG_FILELEN_ERROR "Error in %s for %s" +#define MSG_FILE_CLOSE_ERR "Error %d occurred closing the file" +#define MSG_FILE_IS_EMPTY "File %s is empty" +#define MSG_FILE_MAP_ERR "File mapping error" +#define MSG_FILE_MAP_ERROR "CreateFileMapping %s error rc=%d" +#define MSG_FILE_NOT_FOUND "File %s cannot be found" +#define MSG_FILE_OPEN_YET "File %s already open" +#define MSG_FILE_UNFOUND "File %s not found" +#define MSG_FILGRP_NO_TABLE "Missing table %d for a filter group" +#define MSG_FILTER_ATTACH "Filter passed to Attach" +#define MSG_FILTER_NO_TABLE "Missing first table for a filter" +#define MSG_FIND_BAD_TYPE "Array Find type mismatch %s %s" +#define MSG_FIX_OVFLW_ADD "Fixed Overflow on add" +#define MSG_FIX_OVFLW_TIMES "Fixed Overflow on times" +#define MSG_FIX_UNFLW_ADD "Fixed Underflow on add" +#define MSG_FIX_UNFLW_TIMES "Fixed Underflow on times" +#define MSG_FLD_TOO_LNG_FOR "Field %d too long for %s line %d of %s" +#define MSG_FLTST_NO_CORREL "FilTest should be called only for correlated subqueries" +#define MSG_FLT_BAD_RESULT "Float inexact result" +#define MSG_FLT_DENORMAL_OP "Float denormal operand" +#define MSG_FLT_INVALID_OP "Float invalid operation" +#define MSG_FLT_OVERFLOW "Float overflow" +#define MSG_FLT_STACK_CHECK "Float stack check" +#define MSG_FLT_UNDERFLOW "Float underflow" +#define MSG_FLT_ZERO_DIVIDE "Float divide by zero" +#define MSG_FMT_WRITE_NIY "Writing %s files is not implemented yet" +#define MSG_FNC_NOTIN_SLIST "Order aggregate function not in select list" +#define MSG_FORMAT_ERROR "Formating error" +#define MSG_FOXPRO_FILE "FoxPro file: " +#define MSG_FPUTS_ERROR "fputs error: %s" +#define MSG_FSBPARP_NULL "PUTFON: fsbparp is NULL" +#define MSG_FSEEK_ERROR "fseek error: %s" +#define MSG_FSETPOS_ERROR "fseek error for i=%d" +#define MSG_FTELL_ERROR "ftell error for recd=%d: %s" +#define MSG_FUNCTION_ERROR "%s error: %d" +#define MSG_FUNC_ERRNO "Error %d in %s" +#define MSG_FUNC_ERROR "Error in %s" +#define MSG_FUNC_ERR_S "%s error: %s" +#define MSG_FUNC_REF_DEL "Reference to a defined function (rule %d) which has been deleted" +#define MSG_FWRITE_ERROR "fwrite error: %s" +#define MSG_GETCWD_ERR_NO "?getcwd %s errno=%d" +#define MSG_GETFILESIZE_ERR "Error %d in GetFileSize" +#define MSG_GET_DIST_VALS "Retrieving distinct values from " +#define MSG_GET_ERROR "Error in %s (column %d)" +#define MSG_GET_FUNC_ERR "Error getting function %s: %s" +#define MSG_GET_NAME_ERR "Error getting SYS table names" +#define MSG_GLOBAL_ERROR "Cannot allocate Global (size=%d)\n" +#define MSG_GRAM_ALLOC_ERR "Allocation error in Grammar Up" +#define MSG_GRAM_MISMATCH "Warning: GRAMMAR version mismatch (saved under GRAMMAR v%u)" +#define MSG_GRAM_SUBSET_ERR "Grammar dictionary Subset error" +#define MSG_GRBY_TAB_NOTIMP "Group by filtered joined table not implemented" +#define MSG_GROUPBY_NOT_ALL "Group By must include all non-functional select items" +#define MSG_GROUP_ON_FUNC "Invalid group by on functional column" +#define MSG_GRP_COL_MISM "Column mismatch in groups" +#define MSG_GRP_LIST_MISMAT "Grouping does not match select list" +#define MSG_GUARD_PAGE "Guard page violation" +#define MSG_GZOPEN_ERROR "gzopen %s error %d on %s" +#define MSG_GZPUTS_ERROR "gzputs error: %s" +#define MSG_HANDLE_IS_NULL "%s is NULL: last error: %d" +#define MSG_HARRY_COMP_NIY "Compute not implemented for coded strings" +#define MSG_HAVING_FILTER "Filtering by Having" +#define MSG_HBUF_TOO_SMALL "Buffer(%d) too small for header(%d)" +#define MSG_HEAD_OPEN_ERROR "Error opening header file %s" +#define MSG_HEAD_READ_ERROR "Error reading header file %s" +#define MSG_HEAD_WRITE_ERR "Error writing header file %s" +#define MSG_HI_OFFSET_ERR "High offet is not 0" +#define MSG_HUGE_DEFAULT "Huge defaults to %d" +#define MSG_HUGE_WARNING_1 "Huge memory not 16-bit compatible for %d\n" +#define MSG_HUGE_WARNING_2 "Unpredictable results may occur\n" +#define MSG_IDLE "Idle" +#define MSG_ILLEGAL_INSTR "Illegal instruction" +#define MSG_ILL_FILTER_CONV "Filtering implies an illegal conversion" +#define MSG_INDEX_CREATED "Index %s created on %s" +#define MSG_INDEX_DEF_ERR "Error storing index definition for %s" +#define MSG_INDEX_DROPPED "Index %s dropped from %s" +#define MSG_INDEX_INIT_ERR "Cannot initialize index %s" +#define MSG_INDEX_NOT_DEF "Index %s has no definition" +#define MSG_INDEX_NOT_UNIQ "Index is not unique" +#define MSG_INDEX_ONE_SAVE "Indexes are saved in one unique file" +#define MSG_INDEX_SEP_SAVE "Indexes are saved in separate files" +#define MSG_INDEX_YET_ON "Index %s already exists on %s" +#define MSG_INDX_ALL_DROP "All indexes dropped from %s" +#define MSG_INDX_COL_NOTIN "Index column %s is not in table %s" +#define MSG_INDX_EXIST_YET "Index entry already exists" +#define MSG_INIT_ERROR "Error initializing %s" +#define MSG_INIT_FAILED "Failed to initialize %s processing" +#define MSG_INPUT "Input: " +#define MSG_INPUT_KEYBD_YET "Input already from keyboard" +#define MSG_INSERTING "Inserting: " +#define MSG_INSERT_ERROR "Insert error: file %s in use" +#define MSG_INSERT_MISMATCH "Column/Value list mismatch" +#define MSG_INTERNAL "internal" +#define MSG_INT_COL_ERROR "Internal error for index column %s" +#define MSG_INT_OVERFLOW "Integer overflow" +#define MSG_INT_ZERO_DIVIDE "Integer divide by zero" +#define MSG_INVALID_BIP "Invalid Bip .%d" +#define MSG_INVALID_DISP "Invalid disposition" +#define MSG_INVALID_FTYPE "SBV: invalid Ftype %d" +#define MSG_INVALID_HANDLE "Invalid handle" +#define MSG_INVALID_OPER "Invalid operator %d for %s" +#define MSG_INVALID_OPTION "Invalid option %s" +#define MSG_INV_COLUMN_TYPE "Invalid type %d for column %s" +#define MSG_INV_COL_DATATYP "Invalid Data Type %d for column %d" +#define MSG_INV_COL_NUM "Invalid column %d" +#define MSG_INV_COL_TYPE "Invalid column type %s" +#define MSG_INV_CONC_BIP "Invalid bip (only valid are : %.8s.0 .1 and .5)" +#define MSG_INV_DATA_PATH "Invalid database path %s" +#define MSG_INV_DEF_READ "Invalid deferred Read rc=%d" +#define MSG_INV_DIRCOL_OFST "Invalid DIRCOL offset %d" +#define MSG_INV_DOMAIN_TYPE "Invalid type %d" +#define MSG_INV_FILTER "Filter met in %s" +#define MSG_INV_FNC_BUFTYPE "FNC: invalid argument type %d for %s" +#define MSG_INV_INFO_TYPE "Invalid catalog info type %d" +#define MSG_INV_INIPATH "Invalid inipath " +#define MSG_INV_MAP_POS "Invalid map position" +#define MSG_INV_OPERATOR "invalid operator %d\n" +#define MSG_INV_PARAMETER "Invalid parameter %s" +#define MSG_INV_PARM_TYPE "Invalid parameter type" +#define MSG_INV_QUALIFIER "Invalid qualifier '%s'" +#define MSG_INV_QUERY_TYPE "Invalid query type %d" +#define MSG_INV_RAND_ACC "Invalid random access to non optimized table" +#define MSG_INV_REC_POS "Invalid record position" +#define MSG_INV_RESULT_TYPE "Invalid result type %s" +#define MSG_INV_SET_SUBTYPE "Invalid SetFormat subtype %d" +#define MSG_INV_SPECIAL_CMD "%s: Invalid special command" +#define MSG_INV_SUBTYPE "Invalid subtype %s" +#define MSG_INV_TOK_DOMAIN "Invalid token domain %s" +#define MSG_INV_TOPSEM_CMD "Invalid TopSem command %c" +#define MSG_INV_TRANSF_USE "Invalid use in transformation rule" +#define MSG_INV_TYPE_SPEC "Invalid type specification (%.8s.%d)" +#define MSG_INV_UPDT_TABLE "Table %s invalid for update" +#define MSG_INV_VALUE_LIST "Invalid Insert value list" +#define MSG_INV_WHERE_JOIN "Invalid where clause in join query" +#define MSG_INV_WORK_PATH "Invalid work path %s" +#define MSG_IN_ARGTYPE_MISM "Argument type mismatch for IN expression" +#define MSG_IN_USE " and in use" +#define MSG_IN_WITHOUT_SUB "IN or EXISTS without array or subquery" +#define MSG_IS_NOT_CONN "%s is not a connection definition" +#define MSG_JCT_MISS_COLS "Missing columns for a JCT table" +#define MSG_JCT_MISS_TABLE "Missing joined table for JCT" +#define MSG_JCT_NO_FILTER "Virtual JCT tables cannot be filtered" +#define MSG_JCT_NO_KEY "Logical JCT error: no key" +#define MSG_JOIN_KEY_NO_COL "Join key is not a column" +#define MSG_KEY_ALLOC_ERR "Error allocating Key offset block" +#define MSG_KEY_ALLOC_ERROR "Memory allocation error, Klen=%d n=%d" +#define MSG_LANGUAGE_QUIT "%s quit" +#define MSG_LANG_ACTIVE "Language %s active" +#define MSG_LANG_ALLOC_FAIL "PlugInitLang: Lang block allocation failed" +#define MSG_LANG_ALREADY_UP "Edited language is already there" +#define MSG_LANG_BAD_SAVE "Language %s may be incorrectly saved" +#define MSG_LANG_NOT_FREED "Language %s cannot be freed (not in main chain)" +#define MSG_LANG_SAVED "Language %s saved" +#define MSG_LANG_WR_LEN_ERR "Lang block write length error" +#define MSG_LDF_ALLOC_ERROR "LdfBlock allocation error" +#define MSG_LDF_RN_MISMATCH "LDF rule number mismatch" +#define MSG_LDF_WLEN_ERROR "LdfData write length error" +#define MSG_LDF_W_LEN_ERROR "LdfData write length error" +#define MSG_LIC_NO_MYSQL "Your current license does not enable using the MySQL table type" +#define MSG_LINEAR_ERROR "Linearization error" +#define MSG_LINE_LENGTH "Output line length reset to %d" +#define MSG_LINE_MAXLIN "Max number of work lines reset to %d" +#define MSG_LINE_MAXRES "Max number of output lines reset to %d" +#define MSG_LINE_MAXTMP "Max number of intermediate lines reset to %d" +#define MSG_LINE_TOO_LONG "New line is too long" +#define MSG_LINJOINDB_ERROR "System error: incorrect call to LinJoinDB" +#define MSG_LIST "--List--" +#define MSG_LNG_NOT_IN_LIST "Language %s not found in attached list" +#define MSG_LOADING_DB "Loading DB Description" +#define MSG_LOADING_FAILED "Loading of %s failed" +#define MSG_LOAD_CDLL_ERROR "Error loading ConnDll: rc=%d" +#define MSG_LOCSTRG_TOO_BIG "LOCSTRG: n too big ? (%d)\n" +#define MSG_LOGICAL_ERROR "%s: Logical error" +#define MSG_LRECL_TOO_SMALL "Lrecl too small (headlen = %d)" +#define MSG_MAC_NO_DELETE "Delete not enabled for MAC tables" +#define MSG_MAC_NO_INDEX "No direct access to MAC tables" +#define MSG_MAC_READ_ONLY "MAC tables are read only" +#define MSG_MAC_WIN_ONLY "MAC tables are Windows only" +#define MSG_MAKE_EMPTY_FILE "Making empty file %s: %s" +#define MSG_MAKING "Making" +#define MSG_MAKING_DISTINCT "Making distinct groups" +#define MSG_MALLOC_ERROR "Memory allocation failed: %s returned Null" +#define MSG_MALLOC_NULL "malloc returned Null" +#define MSG_MAP_NO_MORE "Type %s no more supported" +#define MSG_MAP_OBJ_ERR "Error %d occurred closing the mapping object" +#define MSG_MAP_VEC_ONLY "MAP Insert is for VEC Estimate tables only" +#define MSG_MAP_VIEW_ERROR "MapViewOfFile %s error rc=%d" +#define MSG_MAXSIZE_ERROR "Cannot calculate max size on open table" +#define MSG_MAXTMP_TRUNCATE "Intermediate results truncated by maxtmp=%d" +#define MSG_MAX_BITMAP "Max opt bitmap size reset to %d" +#define MSG_MEMSIZE_TOO_BIG "Error: memsize (%d) too big for length (%d)" +#define MSG_MEM_ALLOC_ERR "Memory allocation error, %s size=%d" +#define MSG_MEM_ALLOC_ERROR "Memory allocation error" +#define MSG_MEM_ALLOC_YET "Memory already allocated" +#define MSG_METAFILE_NOTFND "Grammar Meta file not found" +#define MSG_MISPLACED_QUOTE "Misplaced quote in line %d" +#define MSG_MISSING "Missing: Value=%p Argval=%p Builtin=%d" +#define MSG_MISSING_ARG "Missing argument for operator %d" +#define MSG_MISSING_COL_DEF "Missing column definition" +#define MSG_MISSING_CONNECT "Missing connect string" +#define MSG_MISSING_EOL "Missing endline character in %s" +#define MSG_MISSING_FIELD "Missing field %d in %s line %d" +#define MSG_MISSING_FNAME "Missing file name" +#define MSG_MISSING_NODE "Missing %s node in %s" +#define MSG_MISSING_POS "Missing POS code" +#define MSG_MISSING_ROWNODE "Can't find RowNode for row %d" +#define MSG_MISSING_SERV_DB "Missing server and/or database string" +#define MSG_MISS_LEAD_COL "Missing leading index column %s" +#define MSG_MISS_NAME_LRECL "Missing file name and/or lrecl" +#define MSG_MISS_TABLE_LIST "Missing table list" +#define MSG_MISS_VCT_ELMT "Missing VCT block size (Elements)" +#define MSG_MIS_TAG_LIST "Missing column tag list" +#define MSG_MKEMPTY_NIY "MakeEmptyFile: not yet implemented for Huge and Unix" +#define MSG_MOVE_INV_TYPE "MOVPARM: Invalid parameter type %d" +#define MSG_MULT_DISTINCT "Distinct is specified more than once" +#define MSG_MULT_KEY_ERROR "Multiple key error k=%d n=%d" +#define MSG_MUL_MAKECOL_ERR "Tabmul MakeCol logical error" +#define MSG_MYSQL_CNC_OFF "MySQL connection is closed" +#define MSG_MYSQL_CNC_ON "MySQL connection is established" +#define MSG_MYSQL_NOT_SUP "MySQL not supported by this version" +#define MSG_MY_CNC_ALREADY "MySQL connection already active" +#define MSG_NAME_CONV_ERR "Error converting node name" +#define MSG_NAME_IS_USED "Name %s already in use" +#define MSG_NCOL_GT_MAXCOL "Too many columns (%d > %d max)" +#define MSG_NEW_CHAR_NULL "new char(%d) returned Null" +#define MSG_NEW_DOC_FAILED "Cannot create new document" +#define MSG_NEW_RETURN_NULL "New returned Null in PlugEvalLike" +#define MSG_NEW_TABLE_ERR "Unable to retrieve new table %s" +#define MSG_NEXT_FILE_ERROR "Couldn't find next file. rc=%d" +#define MSG_NODEF_FROM_VIEW "Cannot define a table from a view" +#define MSG_NODE_FOR_CHAR "Node %s found when looking for character" +#define MSG_NODE_SUBSET_ERR "Node %d Subset error" +#define MSG_NONCONT_EXCEPT "Noncontinuable exception" +#define MSG_NON_DUP_HAVING "Having clause in non/dup functional query" +#define MSG_NON_EVAL_SEM "Sem not evaluated: p_no=%d" +#define MSG_NOP_ZLIB_INDEX "Cannot do indexing on non optimized zlib table" +#define MSG_NOT_A_DBF_FILE "Not a dBASE dbf file " +#define MSG_NOT_ENOUGH_COLS "Not enough columns in %s" +#define MSG_NOT_ENOUGH_MEM "Not enough memory to perform this operation" +#define MSG_NOT_FIXED_LEN "File %s is not fixed length, len=%d lrecl=%d" +#define MSG_NOT_IMPLEMENTED "Not implemented: %.8s" +#define MSG_NOT_IMPL_JOIN "Not implemented for Join" +#define MSG_NOT_IMPL_SET "Not implemented for set operators" +#define MSG_NOT_IMPL_YET "Not implemented yet" +#define MSG_NOT_LINEARIZED "Table tree was not linearized" +#define MSG_NOT_MODIFIABLE " (not modifiable)" +#define MSG_NO_0DH_HEAD "No 0Dh at end of header (dbc=%d)" +#define MSG_NO_ACTIVE_APPL "No active application" +#define MSG_NO_ACTIVE_DB "No active database" +#define MSG_NO_ACTIVE_UDIC "No active user dictionary" +#define MSG_NO_AGGR_FUNC "Aggregated function %d not allowed here" +#define MSG_NO_AREA_FILE "Area file not found" +#define MSG_NO_AVAIL_RESULT "No result available" +#define MSG_NO_BIG_DELETE "Partial delete not yet implemented for Huge files" +#define MSG_NO_CHAR_FROM "Cannot return char value from type %d" +#define MSG_NO_CLUSTER_COL "No clustered columns" +#define MSG_NO_COL_ADDING "Cannot add new column(s) to old definition" +#define MSG_NO_COL_DEF_AS "Column definitions cannot be used with AS Select" +#define MSG_NO_COL_FOUND "No column found in section %s" +#define MSG_NO_COL_IN_TABLE "Column %d not in table %s" +#define MSG_NO_COL_SECTION "Missing column section for table %s" +#define MSG_NO_CONNECT_ADDR "No connexion address provided" +#define MSG_NO_CONST_FILTER "Constant filters not implemented" +#define MSG_NO_CURLY_BRKT "No closing curly bracket" +#define MSG_NO_DATABASE "Database %s not found" +#define MSG_NO_DATE_FMT "No date format for valblock of type %d" +#define MSG_NO_DBF_INSERT "Insert not supported yet for GDF files" +#define MSG_NO_DEF_FNCCOL "Cannot find default function column" +#define MSG_NO_DEF_PIVOTCOL "Cannot find default pivot column" +#define MSG_NO_DIR_INDX_RD "No direct access of %s tables" +#define MSG_NO_DMY_DIR_ACC "No direct access of virtual DUMMY tables" +#define MSG_NO_DOM_DELETE "Partial delete not yet implemented for domains" +#define MSG_NO_DOM_MATCH "Unmatched string %.8s... in domain %s" +#define MSG_NO_EDITED_LANG "Coparm: No active edited language" +#define MSG_NO_EXP_LINK "Cannot use expression to link a JCT table" +#define MSG_NO_EXT_FILTER "Filtering cannot refer to another table" +#define MSG_NO_EXT_UPDATE "Cannot update with reference to another table" +#define MSG_NO_FEAT_SUPPORT "No %s support in this version" +#define MSG_NO_FILE_LIST "Table %s has no file list" +#define MSG_NO_FLD_FORMAT "Missing format for field %d of %s" +#define MSG_NO_FORMAT_COL "Cannot format the type COLUMN" +#define MSG_NO_FORMAT_TYPE "Cannot set format from type %d" +#define MSG_NO_FULL_JOIN "Only Equi-join on key(s) is allowed by check setting" +#define MSG_NO_FUL_OUT_JOIN "Full outer joins are not supported" +#define MSG_NO_FUNC_ORDER "Unsupported ordering on functional item" +#define MSG_NO_HEAD_JOIN "Join on not heading table" +#define MSG_NO_HQL_CONV "Conversion to HQL not available" +#define MSG_NO_INDEX "No indexes on table %s" +#define MSG_NO_INDEX_GBX "No or improper index for SQLGBX" +#define MSG_NO_INDEX_IN "No indexes found in %s" +#define MSG_NO_INDEX_READ "No indexed read for multiple tables" +#define MSG_NO_INIT_LANG "No initial language" +#define MSG_NO_JOIN_TO_EXP "No join to expressions" +#define MSG_NO_JOIN_UPDEL "Update/Delete on MySQL table cannot be joined" +#define MSG_NO_KEY_COL "No key columns found" +#define MSG_NO_KEY_UPDATE "Cannot update key names" +#define MSG_NO_LANGUAGE "No language in operation\n" +#define MSG_NO_LANG_TO_QUIT "No next language to quit" +#define MSG_NO_LISTVAL_HERE "LSTBLK: List of values used out of context" +#define MSG_NO_MAP_INSERT "MAP incompatible with Insert" +#define MSG_NO_MATCHING_COL "No matching column %s in %s" +#define MSG_NO_MATCH_COL "Cannot find matching column" +#define MSG_NO_MEMORY "No memory" +#define MSG_NO_MEM_CORR_SUB "In memory correlated subquery not implemented yet" +#define MSG_NO_MODE_PADDED "Mode not supported for padded files" +#define MSG_NO_MORE_COL "Column %s no more in pivot table" +#define MSG_NO_MORE_LANG "No more language, exit from %s\n" +#define MSG_NO_MORE_VAR "VAR files no more supported" +#define MSG_NO_MULCOL_JOIN "No join yet on muticolumn index" +#define MSG_NO_MULT_HAVING "Multiple having clauses not implemented" +#define MSG_NO_MUL_DIR_ACC "Direct access of multiple tables not implemented yet" +#define MSG_NO_MUL_VCT "VCT tables cannot be multiple" +#define MSG_NO_MYSQL_CONN "No open MySQL connection" +#define MSG_NO_MYSQL_DELETE "Delete should not be called for MySQL tables" +#define MSG_NO_NBCOL "No NBcol" +#define MSG_NO_NBLIN "No NBlin, MaxSize or Continued" +#define MSG_NO_NBLIN_CONT "Fetch: No NBlin or Continued" +#define MSG_NO_NULL_CONST "Cannot handle <null> constant" +#define MSG_NO_ODBC_COL "Automatic ODBC columns not supported in this version" +#define MSG_NO_ODBC_DELETE "Delete should not be called for ODBC tables" +#define MSG_NO_ODBC_DIRECT "Direct access of ODBC tables not implemented yet" +#define MSG_NO_ODBC_MUL "Multiple(2) not supported for ODBC tables" +#define MSG_NO_ODBC_SPECOL "No ODBC special columns" +#define MSG_NO_OPT_COLUMN "Not optimizable or no optimized columns" +#define MSG_NO_OP_MODIF "Modificators do not apply to %s" +#define MSG_NO_PARAMETER "No parameter" +#define MSG_NO_PART_DEL "No partial delete of %s files" +#define MSG_NO_PART_MAP "Partial mapping not implemented for this OS" +#define MSG_NO_PAR_BLK_INS "Cannot insert partial block yet" +#define MSG_NO_PIV_DIR_ACC "No direct access to PIVOT tables" +#define MSG_NO_POS_ADDED "No Pos_code added" +#define MSG_NO_PROMPTING "Cannot handle prompting for distributed tables" +#define MSG_NO_QRY_DELETE "Delete cannot be used for QRY views" +#define MSG_NO_QUERY_ARRAY "Array from QUERY not implemented yet" +#define MSG_NO_RCUR_DSK_YET "Recursive use of DISK not implemented yet" +#define MSG_NO_READ_32 "Can't read 32 bytes" +#define MSG_NO_RECOV_SPACE "Cannot recover space in index file" +#define MSG_NO_REF_DELETE "Cannot delete with reference to another table" +#define MSG_NO_REF_UPDATE "Cannot update with reference to another table" +#define MSG_NO_REMOTE_FNC "Cannot process some functions remotely" +#define MSG_NO_ROWID_FOR_AM "Can't get RowID in direct access for tables of type %s" +#define MSG_NO_ROW_NODE "Row node name is not defined" +#define MSG_NO_SECTION_NAME "Missing section name" +#define MSG_NO_SEC_UPDATE "Cannot update section names" +#define MSG_NO_SELECTED_DB "No selected database" +#define MSG_NO_SELF_PIVOT "Cannot pivot oneself!" +#define MSG_NO_SERVER_FOUND "No server found" +#define MSG_NO_SETPOS_YET "%s SetPos not implemented yet" +#define MSG_NO_SFEXIT_UNIX "Function %s not available on Unix" +#define MSG_NO_SOURCE " (no source)" +#define MSG_NO_SPEC_COL "No MySQL special columns" +#define MSG_NO_SQL_DELETE "Delete cannot be currently used for SQL views" +#define MSG_NO_SUB_VAL "No sub value for array of type %d" +#define MSG_NO_SUCH_INDEX "No indexes %s on table %s" +#define MSG_NO_SUCH_SERVER "cannot find the server %s" +#define MSG_NO_SUCH_TABLE "Table %s not in DB" +#define MSG_NO_TABCOL_DATA "No data found for table %s column %s" +#define MSG_NO_TABLE_COL "No columns found for %s" +#define MSG_NO_TABLE_DEL "Delete not enabled for %s tables " +#define MSG_NO_TABLE_DESC "No Table Description Block" +#define MSG_NO_TABLE_INDEX "Table %s has no index" +#define MSG_NO_TABLE_LIST "No table list" +#define MSG_NO_TAB_DATA "No data found for table %s" +#define MSG_NO_TERM_IN_TOK "Non terminal cannot be used in token rules" +#define MSG_NO_TOKEN_DB "Cannot find DB for Token column %s" +#define MSG_NO_UNIX_CATINFO "No catalog info under Unix" +#define MSG_NO_UPDEL_JOIN "Update/Delete on ODBC table cannot be joined" +#define MSG_NO_VCT_DELETE "Partial delete not yet implemented for VCT files" +#define MSG_NO_VIEW_COLDEF "No coldefs available for views" +#define MSG_NO_VIEW_SORT "Cannot sort/join SQL functional view %s" +#define MSG_NO_ZIP_DELETE "Delete Zip files not implemented yet" +#define MSG_NO_ZIP_DIR_ACC "Direct access of ZDOS tables not implemented yet" +#define MSG_NULL_COL_VALUE "Column Value block is NULL" +#define MSG_NULL_ENTRY "InitLang, null entry %d %s" +#define MSG_NULL_QUERY "Null query" +#define MSG_NUMVAL_NOMATCH "Numval mismatch for %s" +#define MSG_N_FULL_PARSES "%d full parses" +#define MSG_ODBC_READ_ONLY "ODBC is currently read only" +#define MSG_OFFSET_NOT_SUPP "Offset not implemented for this type of sub query" +#define MSG_ONE_LANG_YET "Already one language in edition" +#define MSG_ONE_PARAM_ONLY "Only one parameter allowed" +#define MSG_ONLY_LOG10_IMPL "Only Log10 is implemented" +#define MSG_ON_LANGUAGE "Language %.8s version %d.%d loaded for editing" +#define MSG_OPENING "Opening" +#define MSG_OPENING_QUERY "Opening query" +#define MSG_OPEN_EMPTY_FILE "Opening empty file %s: %s" +#define MSG_OPEN_ERROR "Open error %d in mode %d on %s: " +#define MSG_OPEN_ERROR_IS "Open error on %s: %s" +#define MSG_OPEN_ERROR_ON "Open error on %s" +#define MSG_OPEN_MODE_ERROR "Open(%s) error %d on %s" +#define MSG_OPEN_SORT_ERROR "Logical sort error in QUERY Open" +#define MSG_OPEN_STRERROR "open error: %s" +#define MSG_OPEN_W_ERROR "Couldn't open %s for writing" +#define MSG_OPTBLK_RD_ERR "Error reading opt block values: %s" +#define MSG_OPTBLK_WR_ERR "Error writing opt block values: %s" +#define MSG_OPTIMIZING "Optimizing " +#define MSG_OPT_BMAP_RD_ERR "Error reading opt bitmaps: %s" +#define MSG_OPT_BMAP_WR_ERR "Error writing opt bitmaps: %s" +#define MSG_OPT_CANCELLED "Optimize cancelled by User" +#define MSG_OPT_DVAL_RD_ERR "Error reading distinct values: %s" +#define MSG_OPT_DVAL_WR_ERR "Error writing distinct values: %s" +#define MSG_OPT_HEAD_RD_ERR "Error reading opt file header: %s" +#define MSG_OPT_HEAD_WR_ERR "Error writing opt file header: %s" +#define MSG_OPT_INIT "Optimization initialized" +#define MSG_OPT_LOGIC_ERR "Logical error in SetBitmap, i=%d" +#define MSG_OPT_MAX_RD_ERR "Error reading opt max values: %s" +#define MSG_OPT_MAX_WR_ERR "Error writing opt max values: %s" +#define MSG_OPT_MIN_RD_ERR "Error reading opt min values: %s" +#define MSG_OPT_MIN_WR_ERR "Error writing opt min values: %s" +#define MSG_OPT_NOT_MATCH "Non-matching opt file %s" +#define MSG_OP_RES_TOO_LONG "Result too long for operator=%d" +#define MSG_ORDER_OUT_RANGE "Order %d out of range" +#define MSG_ORDER_TWICE "Ordering twice the same select item" +#define MSG_PAGE_ERROR "In page error" +#define MSG_PARM_CNT_MISS "Parameter count mismatch" +#define MSG_PARSE_NULL_SEM "Parse with null semantics" +#define MSG_PARSING_QUERY "Parsing query" +#define MSG_PIX_ERROR "Pix %s error Rule_no=%u\n" +#define MSG_PIX_TEST_ERROR "Rule=%u: pix-TEST not in first node\n" +#define MSG_PLG_READ_ONLY "PLG is currently Read Only" +#define MSG_PLM_NULL_SFP "TABPLM ReadDB: Sfp is NULL" +#define MSG_PLUG_NOT_INIT "Plug was not initialized\n" +#define MSG_PLUG_NOT_RUN "Plug is not running" +#define MSG_PNODE_RULE "(P_node %d rule %d) " +#define MSG_POS_TOO_LONG "%s too long (>%d)" +#define MSG_PREC_VBLP_NULL "ARRAY SetPrecision: Vblp is NULL" +#define MSG_PRIV_INSTR "Privileged instruction" +#define MSG_PROCADD_ERROR "Error %d getting address of %s" +#define MSG_PROCESS_SUBQRY "Processing Sub-Query" +#define MSG_PROC_WOULD_LOOP "Process would loop (maxres=%d maxlin=%d)" +#define MSG_PROGRESS_INFO "Progress Information" +#define MSG_PROMPT_CANCEL "Prompt was cancelled" +#define MSG_PROMPT_NIY "Prompt not implemented for this configuration" +#define MSG_PTR_NOT_FOUND "Pointer not found Num=%d ti1=%d" +#define MSG_PXDEF_IS_NULL "Pxdef is NULL" +#define MSG_QRY_READ_ONLY "QRY views are read only" +#define MSG_QUERY_CANCELLED "Query Cancelled by User" +#define MSG_QUERY_NOT_EXEC "Query not executed" +#define MSG_QUERY_SAVED "Query %s saved" +#define MSG_QUOTE_IN_QUOTE "Quote char inside quoted field in line %d" +#define MSG_RANGE_NIY "Range NIY for %s" +#define MSG_RANGE_NO_JOIN "Range is not meant for join index" +#define MSG_RC_READING "rc=%d reading table %s" +#define MSG_READB_BAD_INIT "%s ReadDB called with Init=0" +#define MSG_READCOL_ERROR "SQLCOL ReadColumn error" +#define MSG_READING "Reading" +#define MSG_READING_FROM "Reading from %s" +#define MSG_READING_RECORD "Error reading record %d of %s" +#define MSG_READY "Ready" +#define MSG_READ_ERROR "Error reading %s: %s" +#define MSG_READ_ERROR_RC "Read error, rc=%d" +#define MSG_READ_MEM_ERROR "Reading memory %d: size=%d" +#define MSG_READ_ONLY "Cannot modify this read/only protected table" +#define MSG_READ_SEEK_ERROR "Read seek error: %s" +#define MSG_READ_SEG_ERROR "Reading segment %d: size=%d" +#define MSG_RECEIVED "Received %c\n" +#define MSG_RECORD_ERROR "Error reading record %d of %s" +#define MSG_RECORD_NO_SEP "Record with no separator" +#define MSG_REC_SKIPPED " (%d bad records skipped by MaxErr setting)" +#define MSG_REDUCE_INDEX "Reducing index" +#define MSG_REGISTER_ERR "Unable to register NS with prefix='%s' and href='%s'" +#define MSG_REMOTE_CONN_ERR "Remote connection failed" +#define MSG_REMOVE_ERROR "Error removing %s: %s" +#define MSG_REMOVE_NOT_IMPL "Remove not implemented for non-table TDB" +#define MSG_RENAME_ERROR "Error renaming %s to %s: %s" +#define MSG_RENUM_RULES "Renumber rules and enter ADD again (rule saved in buffer)" +#define MSG_REORDER_INDEX "Reordering index" +#define MSG_REQU_ARG_NUM "Function %s requires %d arguments" +#define MSG_RESET_TO "%s reset to %d" +#define MSG_RES_NOT_UNIQUE "Result is not a unique value" +#define MSG_RET_FROM_LANG "Return to language %s version %d.%d from language %s version %d.%d" +#define MSG_ROWID_NOT_IMPL "RowNumber not implemented for tables of type %s" +#define MSG_ROWS_SELECTED "%d rows selected in %.2lf sec" +#define MSG_ROWS_TRUNCATED " (truncated by MAXRES, LIMIT, FREQ or AreaSize setting)" +#define MSG_ROW_ARGNB_ERR "ROW arg number mismatch (%d,%d)" +#define MSG_RPC_SERVER_ERR "RPC error, server not responding" +#define MSG_RSC_ALLOC_ERROR "Memory allocation error in Rescol %s" +#define MSG_RULE_ENTERED "Rule %d entered" +#define MSG_RULE_SUBSET_ERR "Rules Subset error" +#define MSG_SAVING_INDEX "Saving index file" +#define MSG_SCAN_NOT_IMP "Scan not implemented" +#define MSG_SEC_KEY_FIRST "Section and key names must come first on Insert" +#define MSG_SEC_NAME_FIRST "Section name must come first on Insert" +#define MSG_SEC_NOT_FOUND "Section %s not found in %s" +#define MSG_SEEK_ERROR "Seek error in CopyHeader" +#define MSG_SEMANTIC_TREE "Semantic Tree" +#define MSG_SEM_BAD_REF "Sem name @%d refers to an argument of type not 0 or 1" +#define MSG_SEM_UNKNOWN "unknown, rc=%d" +#define MSG_SEP_IN_FIELD "Field %d contains the separator character" +#define MSG_SEQUENCE_ERROR "Sequence error on statement allocation" +#define MSG_SETEOF_ERROR "Error %d in SetEndOfFile" +#define MSG_SETRECPOS_NIY "SetRecpos not implemented for this table type" +#define MSG_SET_LOCALE "Locale set to %s" +#define MSG_SET_NULL_DOM "Setting value %d to a null domain" +#define MSG_SET_OP_NOT_IMPL "sorry - set operators not implemented" +#define MSG_SET_STR_TRUNC "SetValue: String would be truncated" +#define MSG_SEVERAL_TREES "Some tables are not properly joined" +#define MSG_SFP_ERROR "SetFilePointer error: %s" +#define MSG_SFUNC_NOT_IMPL "Scalar Function %s not implemented" +#define MSG_SHARED_LIB_ERR "Error loading shared library %s: %s" +#define MSG_SINGLE_STEP "Single step" +#define MSG_SLEEP "I slept %d milliseconds" +#define MSG_SMART_SORTING "Retrieving sorted rows (pass %d of %d)" +#define MSG_SMART_SORT_ERR "Logical Smart Sort Error 1" +#define MSG_SORTING "Sorting" +#define MSG_SORTING_INDEX "Sorting index" +#define MSG_SORTING_VAL "Sorting %d values" +#define MSG_SORT_JOIN_INDEX "Sorting join index" +#define MSG_SPCOL_READONLY "Special column %s is Read Only" +#define MSG_SPEC_CMD_SEP "Special commands must be executed separately" +#define MSG_SQL_BAD_TYPE "RephraseSQL: type %d not supported" +#define MSG_SQL_BLOCK_MISM "CheckColumn: SQL current blocks mismatch" +#define MSG_SQL_CONF_ERROR "SQL Error: SQL_CONFORMANCE" +#define MSG_SQL_READ_ONLY "SQL views are currently read only" +#define MSG_SRCH_CLOSE_ERR "Couldn't close search handle" +#define MSG_SRC_TABLE_UNDEF "Source table is not defined" +#define MSG_STACK_ERROR "stack error, i=%d\n" +#define MSG_STACK_OVERFLOW "Parser: Stack overflow\n" +#define MSG_STRG_NOT_FOUND "String not found" +#define MSG_STRING_INV_LIST "List invalid for SemString" +#define MSG_STRING_TOO_BIG "String too big for domain %s" +#define MSG_SUBALLOC_ERROR "Not enough memory in area %p for request of %d (used=%d free=%d)" +#define MSG_SUBAL_HUGE_ERR "Not enough memory in huge %p for request of %d" +#define MSG_SUBARG_NOSEM "@ or sub-phrase arg of level %d points to a meaningless argument" +#define MSG_SUBARG_OUTRANGE "Out of range @ or sub-phrase argument of level %d" +#define MSG_SUBQRY_ONEITEM "Sub-Query must have exactly one select item" +#define MSG_SUBSET_ERROR "SubSet error in LoadDB" +#define MSG_SUB_OPEN_YET "Subquery already open" +#define MSG_SUB_RES_TOO_LNG "Result too long for SUBSTR" +#define MSG_SYNTAX_ERROR "Syntax error" +#define MSG_SYSTEM_ERROR "System error %d" +#define MSG_S_ACCESS_DENIED "%s: access denied" +#define MSG_S_ERROR "%s error" +#define MSG_S_ERROR_NUM "%s: error=%d" +#define MSG_S_INTRUPT_ERROR "%s: interrupt error" +#define MSG_S_INVALID_PARM "%s: invalid parameter" +#define MSG_S_INV_ADDRESS "%s: invalid address" +#define MSG_S_UNKNOWN_ERROR "%s: unknown error code %u" +#define MSG_TABDIR_READONLY "DIR tables are read/only" +#define MSG_TABLE_ALREADY "Table %s already exists" +#define MSG_TABLE_ALTERED "%s table %s altered" +#define MSG_TABLE_CREATED "%s table %s created" +#define MSG_TABLE_DROPPED "Table %s dropped" +#define MSG_TABLE_MULT_JOIN "Table %s used more than once for join" +#define MSG_TABLE_NOT_IN_DB "Table %s does not exist in %s" +#define MSG_TABLE_NOT_OPT "Not an optimizable table" +#define MSG_TABLE_NO_INDEX "Table %s cannot be indexed" +#define MSG_TABLE_NO_OPT "Table %s does not exist or type is not optimizable" +#define MSG_TABLE_READ_ONLY "%s tables are read only " +#define MSG_TABMUL_READONLY "Multiple tables are read/only" +#define MSG_TAB_NOT_LOADED " (some tables could not be loaded)" +#define MSG_TAB_NOT_SPEC "No table specified" +#define MSG_TB_VW_NOTIN_DB "Table or view %s not in DB" +#define MSG_TDB_NXT_NOT_NUL "Tdb.Next not NULL" +#define MSG_TDB_USE_ERROR "Error, Tdbp->Use=%d" +#define MSG_TOO_MANY_COLS "Too many columns" +#define MSG_TOO_MANY_COLTAB "Too many columns in %s (%d)" +#define MSG_TOO_MANY_FIELDS "Too many fields line %d of %s" +#define MSG_TOO_MANY_JUMPS "Too many jump levels" +#define MSG_TOO_MANY_KEYS "Too many keys (%d)" +#define MSG_TOO_MANY_POS "Too many pos_codes" +#define MSG_TOO_MANY_TABLES "Too many tables (%d)" +#define MSG_TOPSEM_ERROR "Unknown error in TopSem" +#define MSG_TO_BLK_IS_NULL "To Blk is NULL" +#define MSG_TO_FTR_NOT_NULL "Set.To_Ftr is not null" +#define MSG_TO_PIX_NOT_NULL "Set.To_Pix is not null" +#define MSG_TO_SEM_NOT_NULL "Set.To_Sem is not null" +#define MSG_TRUNCATE_ERROR "truncate error: %s" +#define MSG_TRUNC_BY_ESTIM "truncated by Estimate" +#define MSG_TYPES_ERROR "Error on Types(%d)" +#define MSG_TYPE_CONV_ERROR "Type cannot be converted in expression" +#define MSG_TYPE_DEF_MISM "type and definition do not match" +#define MSG_TYPE_MISMATCH "Key and source are not of the same type" +#define MSG_TYPE_RECFM_MISM "Type and Recfm mismatch" +#define MSG_TYPE_TO_VERIFY "Type to verify: %d" +#define MSG_TYPE_VALUE_ERR "Column %s type(%s)/value(%s) mismatch" +#define MSG_UNBALANCE_QUOTE "Unbalanced quote in line %d" +#define MSG_UNDEFINED_AM "COLBLK %s: undefined Access Method" +#define MSG_UNDEFINED_PATH "Undefined Plgcnx.ini path" +#define MSG_UNDEF_COL_COUNT "Count on undefined column" +#define MSG_UNKNOWN_DOMAIN "Unknown domain %s" +#define MSG_UNKNOWN_ERROR "Unknown error" +#define MSG_UNKNOWN_EXCPT "Unknown exception" +#define MSG_UNKNOWN_NAME "Unknown name: %.8s" +#define MSG_UNKNOWN_PATH "Unknown Plgcnx.ini path" +#define MSG_UNKNOWN_POS "Unknown pos name: %s" +#define MSG_UNKNOWN_SEM "Unknown Sem %.8s, rc=%d" +#define MSG_UNKNOWN_SYNONYM "Unknown synonym" +#define MSG_UNKNW_QRY_TYPE "ReadDB: unknown query type" +#define MSG_UNKN_ERR_CODE "Unknown error code %d" +#define MSG_UNLOADABLE " unloadable: " +#define MSG_UNLOADABLE_PRM "%s unloadable: %s" +#define MSG_UNMATCH_FIL_ARG "Unmatched filter argument" +#define MSG_UNQ_COL_SEV_TAB "Unqualified column %s is in several tables" +#define MSG_UNRESOLVED_ARG "?Unresolved argument %s at %d line %d" +#define MSG_UPDATE_ERROR "Error updating %s" +#define MSG_UPDATING_ROWS "Updating rows" +#define MSG_UPD_ZIP_NOT_IMP "Updating ZDOS tables not implemented yet" +#define MSG_UP_LANGUAGE "Block language %.8s version %d level %d loaded" +#define MSG_USED_FREE_MEM "%d used in sarea, %d free" +#define MSG_USETEMP_IS "UseTemp is %s" +#define MSG_USETEMP_RESET ". Usetemp reset to Auto" +#define MSG_USETEMP_SET "UseTemp set to %s" +#define MSG_USE_NO_MATCH "Use do not match : Use=%d, ti2=%d, ti3=%d" +#define MSG_USING_INDEX " (Using index" +#define MSG_VALIST_MISMATCH "List of values mismatch" +#define MSG_VALSTR_TOO_LONG "Value %s too long for string of length %d" +#define MSG_VALTYPE_NOMATCH "Non matching Value types" +#define MSG_VALUE_ERROR "Column %s: value is null" +#define MSG_VALUE_NOT_ALLOC "Value not allocated for column R%d %s" +#define MSG_VALUE_TOO_BIG "Value %lld too big for column %s" +#define MSG_VALUE_TOO_LONG "Value %s too long for column %s of length %d" +#define MSG_VAL_ALLOC_ERR "Cannot allocate value node" +#define MSG_VAL_TOO_LONG "Value field %s too long for %s" +#define MSG_VIEW_ALREADY "View %s already exists" +#define MSG_VIEW_CREATED "%s view %s created" +#define MSG_VIEW_DROPPED "View %s dropped" +#define MSG_VIEW_NOT_IN_DB "View %s does not exist in %s" +#define MSG_VIR_NO_DELETE "Delete not allowed for %s tables" +#define MSG_VIR_READ_ONLY "Virtual %s tables are read only" +#define MSG_VM_LANG "Language has VM format, not supported" +#define MSG_VOID_FIRST_ARG "First argument should not be void" +#define MSG_VOID_IN_STRING "Error: void IN string" +#define MSG_VOID_ORDER_LIST "Null ordering list, system error ?" +#define MSG_VOID_POS_DICT "Void Pos dictionary" +#define MSG_VOID_QUERY "Void query %s" +#define MSG_WORK_AREA "Work area: %s" +#define MSG_WORK_TOO_SMALL "Work area too small, increase AreaSize" +#define MSG_WRITE_ERROR "Error writing to %s" +#define MSG_WRITE_SEEK_ERR "Write seek error: %s" +#define MSG_WRITE_STRERROR "Error writing %s: %s" +#define MSG_WRITING "Writing" +#define MSG_WRITING_ERROR "Error writing to %s: %s" +#define MSG_WRITING_QUERY "Writing query: " +#define MSG_WRONG_ARG_NUM "Function %s does not take %d arguments" +#define MSG_WRONG_COL_NUM "Column number %d out of range in %s" +#define MSG_WRONG_DB_LIST "Wrong or nul database list" +#define MSG_WRONG_FUNCTION "Wrong function %d" +#define MSG_WRONG_OP_PARM "Wrong operator or parameters for %s" +#define MSG_WRONG_PARMS "Wrong parameters for %s" +#define MSG_WRONG_PASSWORD "Illegal password for %s" +#define MSG_WRONG_TYPE "unsupported type" +#define MSG_WRONG_USERFILE "Wrong Userfile size=%d" +#define MSG_WS_CONV_ERR "Error converting %s to WS" +#define MSG_XCOL_MISMATCH "Column %s mismatch in index" +#define MSG_XDB_DEL_ERROR "Error while deleting entries from XDB file" +#define MSG_XFILE_READERR "Error %d reading index file" +#define MSG_XFILE_TOO_SMALL "Index file is smaller than index length" +#define MSG_XFILE_WRITERR "Error writing index file: %s" +#define MSG_XMLTAB_INIT_ERR "Error initializing XML table" +#define MSG_XML_INIT_ERROR "Error initializing new XML file" +#define MSG_XPATH_CNTX_ERR "Unable to create new XPath context" +#define MSG_XPATH_EVAL_ERR "Unable to evaluate xpath location '%s'" +#define MSG_XPATH_NOT_SUPP "Unsupported Xpath for column %s" +#define MSG_X_ARG_ADDED "%d arguments have been added" +#define MSG_X_ARG_SET "%d arguments have been set" +#define MSG_X_ON_TAB " %s on %s(" +#define MSG_ZERO_DIVIDE "Zero divide in expression" diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp new file mode 100644 index 00000000000..5a67c5d2dd2 --- /dev/null +++ b/storage/connect/filamap.cpp @@ -0,0 +1,690 @@ +/*********** File AM Map C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMAP */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the MAP file access method classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/* Note: these files are included inside the include files below. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "osutil.h" +#include "maputil.h" +#include "filamap.h" +#include "tabdos.h" + +/* --------------------------- Class MAPFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +MAPFAM::MAPFAM(PDOSDEF tdp) : TXTFAM(tdp) + { + Memory = NULL; + Mempos = NULL; + Tpos = NULL; + Fpos = NULL; + Spos = NULL; + Top = NULL; + } // end of MAPFAM standard constructor + +MAPFAM::MAPFAM(PMAPFAM tmfp) : TXTFAM(tmfp) + { + Memory = tmfp->Memory; + Mempos = tmfp->Mempos; + Fpos = tmfp->Fpos; + Spos = tmfp->Spos; + Tpos = tmfp->Tpos; + Top = tmfp->Top; + } // end of MAPFAM copy constructor + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void MAPFAM::Reset(void) + { + TXTFAM::Reset(); + Fpos = Tpos = Spos = NULL; + } // end of Reset + +/***********************************************************************/ +/* MAP GetFileLength: returns file size in number of bytes. */ +/***********************************************************************/ +int MAPFAM::GetFileLength(PGLOBAL g) + { + int len; + + len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g); + +#ifdef DEBTRACE + htrc("Mapped file length=%d\n", len); +#endif + + return len; + } // end of GetFileLength + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file as a mapped file. */ +/***********************************************************************/ +bool MAPFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + int len; + MODE mode = Tdbp->GetMode(); + PFBLOCK fp; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + +#if defined(_DEBUG) + // Insert mode is no more handled using file mapping + assert(mode != MODE_INSERT); +#endif // _DEBUG + + /*********************************************************************/ + /* We used the file name relative to recorded datapath. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + /*********************************************************************/ + /* Under Win32 the whole file will be mapped so we can use it as */ + /* if it were entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dbuserp->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + +#ifdef DEBTRACE + htrc("Mapping file, fp=%p\n", fp); +#endif + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memory = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* If required, delete the whole file if no filtering is implied. */ + /*******************************************************************/ + bool del; + HANDLE hFile; + MEMMAP mm; + + del = mode == MODE_DELETE && !Tdbp->GetNext(); + + if (del) + DelRows = Cardinality(g); + + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mode, del); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + +#ifdef DEBTRACE + htrc("%s\n", g->Message); +#endif + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*******************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************/ + len = mm.lenL; + Memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + Tdbp->ResetSize(); + return false; + } // endif len + + if (!Memory) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + +#if defined(WIN32) + if (mode != MODE_DELETE) { +#else // !WIN32 + if (mode == MODE_READ) { +#endif // !WIN32 + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memory; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fb = fp; // Useful when closing + + /*********************************************************************/ + /* The pseudo "buffer" is here the entire file mapping view. */ + /*********************************************************************/ + Fpos = Mempos = Memory; + Top = Memory + len; + +#ifdef DEBTRACE + htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n", + fp, fp->Count, Memory, len, Top); +#endif + + return AllocateBuffer(g); // Useful for DBF files + } // end of OpenTableFile + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int MAPFAM::GetRowID(void) + { + return Rows; + } // end of GetRowID + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int MAPFAM::GetPos(void) + { + return Fpos - Memory; + } // end of GetPos + +/***********************************************************************/ +/* GetNextPos: return the position of next record. */ +/***********************************************************************/ +int MAPFAM::GetNextPos(void) + { + return Mempos - Memory; + } // end of GetNextPos + +/***********************************************************************/ +/* SetPos: Replace the table at the specified position. */ +/***********************************************************************/ +bool MAPFAM::SetPos(PGLOBAL g, int pos) + { + Fpos = Mempos = Memory + pos; + + if (Mempos >= Top || Mempos < Memory) { + strcpy(g->Message, MSG(INV_MAP_POS)); + return true; + } // endif Mempos + + Placed = true; + return false; + } // end of SetPos + +/***********************************************************************/ +/* Record file position in case of UPDATE or DELETE. */ +/***********************************************************************/ +bool MAPFAM::RecordPos(PGLOBAL g) + { + Fpos = Mempos; + return false; + } // end of RecordPos + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int MAPFAM::SkipRecord(PGLOBAL g, bool header) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + // Skip this record + while (*Mempos++ != '\n') ; // What about Unix ??? + + if (Mempos >= Top) + return RC_EF; + + // Update progress information + dup->ProgCur = GetPos(); + + if (header) + Fpos = Tpos = Spos = Mempos; // For Delete + + return RC_OK; + } // end of SkipRecord + +/***********************************************************************/ +/* ReadBuffer: Read one line for a mapped text file. */ +/***********************************************************************/ +int MAPFAM::ReadBuffer(PGLOBAL g) + { + int len; + + // Are we at the end of the memory + if (Mempos >= Top) + return RC_EF; + + if (!Placed) { + /*******************************************************************/ + /* Record file position in case of UPDATE or DELETE. */ + /*******************************************************************/ + Fpos = Mempos; + CurBlk = (int)Rows++; + } else + Placed = false; + + // Immediately calculate next position (Used by DeleteDB) + while (*Mempos++ != '\n') ; // What about Unix ??? + + // Set caller line buffer + len = (Mempos - Fpos) - Ending; + memcpy(Tdbp->GetLine(), Fpos, len); + Tdbp->GetLine()[len] = '\0'; + return RC_OK; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for MAP access method. */ +/***********************************************************************/ +int MAPFAM::WriteBuffer(PGLOBAL g) + { +#if defined(_DEBUG) + // Insert mode is no more handled using file mapping + if (Tdbp->GetMode() == MODE_INSERT) { + strcpy(g->Message, MSG(NO_MAP_INSERT)); + return RC_FX; + } // endif +#endif // _DEBUG + + /*********************************************************************/ + /* Copy the updated record back into the memory mapped file. */ + /*********************************************************************/ + memcpy(Fpos, Tdbp->GetLine(), strlen(Tdbp->GetLine())); + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for MAP (and FIX?) access methods. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int MAPFAM::DeleteRecords(PGLOBAL g, int irc) + { + int n; + +#ifdef DEBTRACE + fprintf(debug, + "MAP DeleteDB: irc=%d mempos=%p tobuf=%p Tpos=%p Spos=%p\n", + irc, Mempos, To_Buf, Tpos, Spos); +#endif + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = Top; +#ifdef DEBTRACE + htrc("Fpos placed at file top=%p\n", Fpos); +#endif + } // endif irc + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if ((n = Fpos - Spos) > 0) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + memmove(Tpos, Spos, n); + Tpos += n; + +#ifdef DEBTRACE + htrc("move %d bytes\n", n); +#endif + } // endif n + + if (irc == RC_OK) { + Spos = Mempos; // New start position + +#ifdef DEBTRACE + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); +#endif + + } else if (To_Fb) { // Can be NULL for deleted files + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* We must firstly Unmap the view and use the saved file handle */ + /* to put an EOF at the end of the copied part of the file. */ + /*******************************************************************/ + PFBLOCK fp = To_Fb; + + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + /*******************************************************************/ + /* Remove extra records. */ + /*******************************************************************/ + n = Tpos - Memory; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + +#ifdef DEBTRACE + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); +#endif + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Table file close routine for MAP access method. */ +/***********************************************************************/ +void MAPFAM::CloseTableFile(PGLOBAL g) + { + PlugCloseFile(g, To_Fb); + To_Fb = NULL; // To get correct file size in Cardinality + +#ifdef DEBTRACE + htrc("MAP Close: closing %s count=%d\n", + To_File, (To_Fb) ? To_Fb->Count : 0); +#endif + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for MAP access method. */ +/***********************************************************************/ +void MAPFAM::Rewind(void) + { + Mempos = Memory; + } // end of Rewind + +/* --------------------------- Class MBKFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +MBKFAM::MBKFAM(PDOSDEF tdp) : MAPFAM(tdp) + { + Blocked = true; + Block = tdp->GetBlock(); + Last = tdp->GetLast(); + Nrec = tdp->GetElemt(); + BlkPos = tdp->GetTo_Pos(); + CurNum = Nrec; + } // end of MBKFAM standard constructor + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void MBKFAM::Reset(void) + { + MAPFAM::Reset(); + CurNum = Nrec; // To start by a new block + } // end of Reset + +/***********************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int MBKFAM::Cardinality(PGLOBAL g) + { + // Should not be called in this version + return (g) ? -1 : 0; +//return (g) ? (int)((Block - 1) * Nrec + Last) : 1; + } // end of Cardinality + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int MBKFAM::SkipRecord(PGLOBAL g, bool header) + { + return RC_OK; + } // end of SkipRecord + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int MBKFAM::GetRowID(void) + { + return CurNum + Nrec * CurBlk + 1; + } // end of GetRowID + +/***********************************************************************/ +/* ReadBuffer: Read one line for a mapped Fix file. */ +/***********************************************************************/ +int MBKFAM::ReadBuffer(PGLOBAL g) + { + int len; + + /*********************************************************************/ + /* Sequential block reading when Placed is not true. */ + /*********************************************************************/ + if (Placed) { + Placed = false; + } else if (Mempos >= Top) { // Are we at the end of the memory + return RC_EF; + } else if (++CurNum < Nrec) { + Fpos = Mempos; + } else { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + if (++CurBlk >= Block) + return RC_EF; + + Fpos = Mempos = Memory + BlkPos[CurBlk]; + } // endif's + + // Immediately calculate next position (Used by DeleteDB) + while (*Mempos++ != '\n') ; // What about Unix ??? + + // Set caller line buffer + len = (Mempos - Fpos) - Ending; + memcpy(Tdbp->GetLine(), Fpos, len); + Tdbp->GetLine()[len] = '\0'; + return RC_OK; + } // end of ReadBuffer + +/***********************************************************************/ +/* Rewind routine for FIX MAP access method. */ +/***********************************************************************/ +void MBKFAM::Rewind(void) + { + Mempos = Memory + Headlen; + CurBlk = -1; + CurNum = Nrec; + } // end of Rewind + +/* --------------------------- Class MPXFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +MPXFAM::MPXFAM(PDOSDEF tdp) : MBKFAM(tdp) + { + Blksize = tdp->GetBlksize(); + Padded = tdp->GetPadded(); + + if (Padded && Blksize) + Nrec = Blksize / Lrecl; + else { + Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; + Blksize = Nrec * Lrecl; + Padded = false; + } // endelse + + CurNum = Nrec; + } // end of MPXFAM standard constructor + +#if 0 // MBKFAM routine is correct +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int MPXFAM::GetRowID(void) + { + return (Mempos - Memory - Headlen) / Lrecl; + } // end of GetRowID +#endif + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int MPXFAM::GetPos(void) + { + return (CurNum + Nrec * CurBlk); // Computed file index + } // end of GetPos + +/***********************************************************************/ +/* SetPos: Replace the table at the specified position. */ +/***********************************************************************/ +bool MPXFAM::SetPos(PGLOBAL g, int pos) + { + if (pos < 0) { + strcpy(g->Message, MSG(INV_REC_POS)); + return true; + } // endif recpos + + CurBlk = pos / Nrec; + CurNum = pos % Nrec; + Fpos = Mempos = Memory + Headlen + pos * Lrecl; + + // Indicate the table position was externally set + Placed = true; + return false; + } // end of SetPos + +/***********************************************************************/ +/* ReadBuffer: Read one line for a mapped Fix file. */ +/***********************************************************************/ +int MPXFAM::ReadBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* Sequential block reading when Placed is not true. */ + /*********************************************************************/ + if (Placed) { + Placed = false; + } else if (Mempos >= Top) { // Are we at the end of the memory + return RC_EF; + } else if (++CurNum < Nrec) { + Fpos = Mempos; + } else { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + if (++CurBlk >= Block) + return RC_EF; + + Fpos = Mempos = Headlen + Memory + CurBlk * Blksize; + } // endif's + + Tdbp->SetLine(Mempos); + + // Immediately calculate next position (Used by DeleteDB) + Mempos += Lrecl; + return RC_OK; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for MAP access method. */ +/***********************************************************************/ +int MPXFAM::WriteBuffer(PGLOBAL g) + { +#if defined(_DEBUG) + // Insert mode is no more handled using file mapping + if (Tdbp->GetMode() == MODE_INSERT) { + strcpy(g->Message, MSG(NO_MAP_INSERT)); + return RC_FX; + } // endif +#endif // _DEBUG + + // In Update mode, file was modified in memory + return RC_OK; + } // end of WriteBuffer + diff --git a/storage/connect/filamap.h b/storage/connect/filamap.h new file mode 100644 index 00000000000..d3503bb0c1d --- /dev/null +++ b/storage/connect/filamap.h @@ -0,0 +1,114 @@ +/*************** FilAMap H Declares Source Code File (.H) **************/ +/* Name: FILAMAP.H Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the MAP file access method classes declares. */ +/***********************************************************************/ +#ifndef __FILAMAP_H +#define __FILAMAP_H + +#include "block.h" +#include "filamtxt.h" + +typedef class MAPFAM *PMAPFAM; + +/***********************************************************************/ +/* This is the variable file access method using file mapping. */ +/***********************************************************************/ +class DllExport MAPFAM : public TXTFAM { + public: + // Constructor + MAPFAM(PDOSDEF tdp); + MAPFAM(PMAPFAM tmfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_MAP;} + virtual int GetPos(void); + virtual int GetNextPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) MAPFAM(this);} + + // Methods + virtual void Reset(void); + virtual int GetFileLength(PGLOBAL g); + virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;} + virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} + virtual int GetRowID(void); + virtual bool RecordPos(PGLOBAL g); + virtual bool SetPos(PGLOBAL g, int recpos); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual bool OpenTableFile(PGLOBAL g); + virtual bool DeferReading(void) {return false;} + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + // Members + char *Memory; // Pointer on file mapping view. + char *Mempos; // Position of next data to read + char *Fpos; // Position of last read record + char *Tpos; // Target Position for delete move + char *Spos; // Start position for delete move + char *Top; // Mark end of file mapping view + }; // end of class MAPFAM + +/***********************************************************************/ +/* This is the blocked file access method using file mapping. */ +/***********************************************************************/ +class DllExport MBKFAM : public MAPFAM { + public: + // Constructor + MBKFAM(PDOSDEF tdp); + MBKFAM(PMAPFAM tmfp) : MAPFAM(tmfp) {} + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) MBKFAM(this);} + + // Methods + virtual void Reset(void); + virtual int Cardinality(PGLOBAL g); + virtual int MaxBlkSize(PGLOBAL g, int s) + {return TXTFAM::MaxBlkSize(g, s);} + virtual int GetRowID(void); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual int ReadBuffer(PGLOBAL g); + virtual void Rewind(void); + + protected: + // No additional members + }; // end of class MBKFAM + +/***********************************************************************/ +/* This is the fixed file access method using file mapping. */ +/***********************************************************************/ +class DllExport MPXFAM : public MBKFAM { + public: + // Constructor + MPXFAM(PDOSDEF tdp); + MPXFAM(PMAPFAM tmfp) : MBKFAM(tmfp) {} + + // Implementation + virtual int GetPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) MPXFAM(this);} + + // Methods + virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);} + virtual int MaxBlkSize(PGLOBAL g, int s) + {return TXTFAM::MaxBlkSize(g, s);} +//virtual int GetRowID(void); + virtual bool SetPos(PGLOBAL g, int recpos); + virtual bool DeferReading(void) {return false;} + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + + protected: + // No additional members + }; // end of class MPXFAM + +#endif // __FILAMAP_H diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp new file mode 100644 index 00000000000..8b3a31579eb --- /dev/null +++ b/storage/connect/filamdbf.cpp @@ -0,0 +1,993 @@ +/*********** File AM Dbf C++ Program Source Code File (.CPP) ****************/ +/* PROGRAM NAME: FILAMDBF */ +/* ------------- */ +/* Version 1.6 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the DBF file access method classes. */ +/* */ +/* ACKNOWLEDGEMENT: */ +/* ---------------- */ +/* Somerset Data Systems, Inc. (908) 766-5845 */ +/* Version 1.2 April 6, 1991 */ +/* Programmer: Jay Parsons */ +/****************************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +//#include <errno.h> +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#else // !UNIX +//#include <io.h> +#endif // !UNIX +//#include <fcntl.h> +#endif // !WIN32 +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "filamdbf.h" +#include "tabdos.h" +#include "valblk.h" +#define NO_FUNC +#include "plgcnx.h" // For DB types +#include "resource.h" + +/****************************************************************************/ +/* Definitions. */ +/****************************************************************************/ +#define HEADLEN 32 /* sizeof ( mainhead or thisfield ) */ +//efine MEMOLEN 10 /* length of memo field in .dbf */ +#define DBFTYPE 3 /* value of bits 0 and 1 if .dbf */ +#define EOH 0x0D /* end-of-header marker in .dbf file */ + +extern "C" int trace; // The general trace value + +/****************************************************************************/ +/* First 32 bytes of a .dbf file. */ +/* Note: some reserved fields are used here to store info (Fields) */ +/****************************************************************************/ +typedef struct _dbfheader { +//uchar Dbf :2; /* both 1 for dBASE III or IV .dbf */ +//uchar :1; +//uchar Db4dbt:1; /* 1 if a dBASE IV-type .dbt exists */ +//uchar Dbfox :4; /* FoxPro if equal to 3 */ + uchar Version; /* Version information flags */ + char Filedate[3]; /* date, YYMMDD, binary. YY=year-1900 */ + uint Records; /* records in the file */ + ushort Headlen; /* bytes in the header */ + ushort Reclen; /* bytes in a record */ + ushort Fields; /* Reserved but used to store fields */ + char Incompleteflag; /* 01 if incomplete, else 00 */ + char Encryptflag; /* 01 if encrypted, else 00 */ + char Reserved2[12]; /* for LAN use */ + char Mdxflag; /* 01 if production .mdx, else 00 */ + char Language; /* Codepage */ + char Reserved3[2]; + } DBFHEADER; + +/****************************************************************************/ +/* Column field descriptor of a .dbf file. */ +/****************************************************************************/ +typedef struct _descriptor { + char Name[11]; /* field name, in capitals, null filled*/ + char Type; /* field type, C, D, F, L, M or N */ + uint Offset; /* used in memvars, not in files. */ + uchar Length; /* field length */ + uchar Decimals; /* number of decimal places */ + short Reserved4; + char Workarea; /* ??? */ + char Reserved5[2]; + char Setfield; /* ??? */ + char Reserved6[7]; + char Mdxfield; /* 01 if tag field in production .mdx */ + } DESCRIPTOR; + +/****************************************************************************/ +/* dbfhead: Routine to analyze a .dbf header. */ +/* Parameters: */ +/* PGLOBAL g -- pointer to the Plug Global structure */ +/* FILE *file -- pointer to file to analyze */ +/* PSZ fn -- pathname of the file to analyze */ +/* DBFHEADER *buf -- pointer to _dbfheader structure */ +/* Returns: */ +/* RC_OK, RC_NF, RC_INFO, or RC_FX if error. */ +/* Side effects: */ +/* Moves file pointer to byte 32; fills buffer at buf with */ +/* first 32 bytes of file. */ +/****************************************************************************/ +static int dbfhead(PGLOBAL g, FILE *file, PSZ fn, DBFHEADER *buf) + { + char endmark[2]; + int dbc = 2, rc = RC_OK; + + *g->Message = '\0'; + + // Read the first 32 bytes into buffer + if (fread(buf, HEADLEN, 1, file) != 1) { + strcpy(g->Message, MSG(NO_READ_32)); + return RC_NF; + } // endif fread + + // Check first byte to be sure of .dbf type + if ((buf->Version & 0x03) != DBFTYPE) { + strcpy(g->Message, MSG(NOT_A_DBF_FILE)); + rc = RC_INFO; + + if ((buf->Version & 0x30) == 0x30) { + strcpy(g->Message, MSG(FOXPRO_FILE)); + dbc = 264; // FoxPro database container + } // endif Version + + } else + strcpy(g->Message, MSG(DBASE_FILE)); + + // Check last byte(s) of header + if (fseek(file, buf->Headlen - dbc, SEEK_SET) != 0) { + sprintf(g->Message, MSG(BAD_HEADER), fn); + return RC_FX; + } // endif fseek + + if (fread(&endmark, 2, 1, file) != 1) { + strcpy(g->Message, MSG(BAD_HEAD_END)); + return RC_FX; + } // endif fread + + // Some files have just 1D others have 1D00 following fields + if (endmark[0] != EOH && endmark[1] != EOH) { + sprintf(g->Message, MSG(NO_0DH_HEAD), dbc); + + if (rc == RC_OK) + return RC_FX; + + } // endif endmark + + // Calculate here the number of fields while we have the dbc info + buf->Fields = (buf->Headlen - dbc - 1) / 32; + fseek(file, HEADLEN, SEEK_SET); + return rc; + } // end of dbfhead + +/* -------------------------- Function DBFColumns ------------------------- */ + +/****************************************************************************/ +/* DBFColumns: constructs the result blocks containing the description */ +/* of all the columns of a DBF file that will be retrieved by #GetData. */ +/****************************************************************************/ +PQRYRES DBFColumns(PGLOBAL g, char *fn, BOOL info) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, + FLD_PREC, FLD_LENGTH, FLD_SCALE}; + static unsigned int length[] = {11, 6, 8, 10, 10, 6}; + char buf[2], filename[_MAX_PATH]; + int ncol = sizeof(dbtype) / sizeof(int); + int rc, type, len, field, fields; + BOOL bad; + DBFHEADER mainhead; + DESCRIPTOR thisfield; + FILE *infile; + PQRYRES qrp; + PCOLRES crp; + + if (trace) + htrc("DBFColumns: File %s\n", SVP(fn)); + + if (!info) { + if (!fn) { + strcpy(g->Message, MSG(MISSING_FNAME)); + return NULL; + } // endif fn + + /************************************************************************/ + /* Open the input file. */ + /************************************************************************/ + PlugSetPath(filename, fn, PlgGetDataPath(g)); + + if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "rb"))) + return NULL; + + /************************************************************************/ + /* Get the first 32 bytes of the header. */ + /************************************************************************/ + if ((rc = dbfhead(g, infile, filename, &mainhead)) == RC_FX) { + fclose(infile); + return NULL; + } // endif dbfhead + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + fields = mainhead.Fields; + } else + fields = 0; + + qrp = PlgAllocResult(g, ncol, fields, IDS_COLUMNS + 3, + dbtype, buftyp, fldtyp, length, true, false); +//qrp->Info = info || (rc == RC_INFO); + + if (info) + return qrp; + + if (trace) { + htrc("Structure of %s\n", filename); + htrc("headlen=%hd reclen=%hd degree=%d\n", + mainhead.Headlen, mainhead.Reclen, fields); + htrc("flags(iem)=%d,%d,%d cp=%d\n", mainhead.Incompleteflag, + mainhead.Encryptflag, mainhead.Mdxflag, mainhead.Language); + htrc("%hd records, last changed %02d/%02d/%d\n", + mainhead.Records, mainhead.Filedate[1], mainhead.Filedate[2], + mainhead.Filedate[0] + (mainhead.Filedate[0] <= 30) ? 2000 : 1900); + htrc("Field Type Offset Len Dec Set Mdx\n"); + } // endif trace + + buf[1] = '\0'; + + /**************************************************************************/ + /* Do it field by field. We are at byte 32 of file. */ + /**************************************************************************/ + for (field = 0; field < fields; field++) { + bad = FALSE; + + if (fread(&thisfield, HEADLEN, 1, infile) != 1) { + sprintf(g->Message, MSG(ERR_READING_REC), field+1, fn); + goto err; + } else + len = thisfield.Length; + + if (trace) + htrc("%-11s %c %6ld %3d %2d %3d %3d\n", + thisfield.Name, thisfield.Type, thisfield.Offset, len, + thisfield.Decimals, thisfield.Setfield, thisfield.Mdxfield); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + switch (thisfield.Type) { + case 'C': // Characters + case 'L': // Logical 'T' or 'F' + type = TYPE_STRING; + break; + case 'N': + type = (thisfield.Decimals) ? TYPE_FLOAT + : (len > 10) ? TYPE_BIGINT : TYPE_INT; + break; + case 'F': + type = TYPE_FLOAT; + break; + case 'D': + type = TYPE_DATE; // Is this correct ??? + break; + default: + if (!info) { + sprintf(g->Message, MSG(BAD_DBF_TYPE), thisfield.Type); + goto err; + } // endif info + + type = TYPE_ERROR; + bad = TRUE; + } // endswitch Type + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(thisfield.Name, field); + crp = crp->Next; // Data Type + crp->Kdata->SetValue((int)type, field); + crp = crp->Next; // Type Name + + if (bad) { + buf[0] = thisfield.Type; + crp->Kdata->SetValue(buf, field); + } else + crp->Kdata->SetValue(GetTypeName(type), field); + + crp = crp->Next; // Precision + crp->Kdata->SetValue((int)thisfield.Length, field); + crp = crp->Next; // Length + crp->Kdata->SetValue((int)thisfield.Length, field); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue((int)thisfield.Decimals, field); + } // endfor field + + qrp->Nblin = field; + fclose(infile); + +#if 0 + if (info) { + /************************************************************************/ + /* Prepare return message for dbfinfo command. */ + /************************************************************************/ + char buf[64]; + + sprintf(buf, + "Ver=%02x ncol=%hu nlin=%u lrecl=%hu headlen=%hu date=%02d/%02d/%02d", + mainhead.Version, fields, mainhead.Records, mainhead.Reclen, + mainhead.Headlen, mainhead.Filedate[0], mainhead.Filedate[1], + mainhead.Filedate[2]); + + strcat(g->Message, buf); + } // endif info +#endif // 0 + + /**************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /**************************************************************************/ + return qrp; + + err: + fclose(infile); + return NULL; + } // end of DBFColumns + +/* ---------------------------- Class DBFBASE ----------------------------- */ + +/****************************************************************************/ +/* Constructors. */ +/****************************************************************************/ +DBFBASE::DBFBASE(PDOSDEF tdp) + { + Records = 0; + Nerr = 0; + Maxerr = tdp->Maxerr; + Accept = tdp->Accept; + ReadMode = tdp->ReadMode; + } // end of DBFBASE standard constructor + +DBFBASE::DBFBASE(DBFBASE *txfp) + { + Records = txfp->Records; + Nerr = txfp->Nerr; + Maxerr = txfp->Maxerr; + Accept = txfp->Accept; + ReadMode = txfp->ReadMode; + } // end of DBFBASE copy constructor + +/****************************************************************************/ +/* ScanHeader: scan the DBF file header for number of records, record size,*/ +/* and header length. Set Records, check that Reclen is equal to lrecl and */ +/* return the header length or 0 in case of error. */ +/****************************************************************************/ +int DBFBASE::ScanHeader(PGLOBAL g, PSZ fname, int lrecl, char *defpath) + { + int rc; + char filename[_MAX_PATH]; + DBFHEADER header; + FILE *infile; + + /************************************************************************/ + /* Open the input file. */ + /************************************************************************/ + PlugSetPath(filename, fname, defpath); + + if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "rb"))) + return 0; // Assume file does not exist + + /************************************************************************/ + /* Get the first 32 bytes of the header. */ + /************************************************************************/ + rc = dbfhead(g, infile, filename, &header); + fclose(infile); + + if (rc == RC_NF) { + Records = 0; + return 0; + } else if (rc == RC_FX) + return -1; + + if ((int)header.Reclen != lrecl) { + sprintf(g->Message, MSG(BAD_LRECL), lrecl, header.Reclen); + return -1; + } // endif Lrecl + + Records = (int)header.Records; + return (int)header.Headlen; + } // end of ScanHeader + +/* ---------------------------- Class DBFFAM ------------------------------ */ + +/****************************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/****************************************************************************/ +int DBFFAM::Cardinality(PGLOBAL g) + { + if (!g) + return 1; + + if (!Headlen) + if ((Headlen = ScanHeader(g, To_File, Lrecl, Tdbp->GetPath())) < 0) + return -1; // Error in ScanHeader + + // Set number of blocks for later use + Block = (Records > 0) ? (Records + Nrec - 1) / Nrec : 0; + return Records; + } // end of Cardinality + +#if 0 // Not compatible with ROWID block optimization +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int DBFFAM::GetRowID(void) + { + return Rows; + } // end of GetRowID +#endif + +/***********************************************************************/ +/* OpenTableFile: Open a DBF table file using C standard I/Os. */ +/* Binary mode cannot be used on Insert because of EOF (CTRL+Z) char. */ +/***********************************************************************/ +bool DBFFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; +//int ftype = Tdbp->GetFtype(); + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = -1; // Means all lines deleted +// DelRows = Cardinality(g); no good because of soft deleted lines + + // This will erase the entire file + strcpy(opmode, "w"); + Tdbp->ResetSize(); + Records = 0; + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "rb" : "r+b"); + break; + case MODE_INSERT: + // Must be in text mode to remove an eventual EOF character + strcpy(opmode, "a+"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + // Now open the file stream + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream = PlugOpenFile(g, filename, opmode))) { +#ifdef DEBTRACE + htrc("%s\n", g->Message); +#endif + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Stream + +#ifdef DEBTRACE + htrc("File %s is open in mode %s\n", filename, opmode); +#endif + + To_Fb = dbuserp->Openlist; // Keep track of File block + + /*********************************************************************/ + /* Allocate the line buffer. For mode Delete a bigger buffer has to */ + /* be allocated because is it also used to move lines into the file.*/ + /*********************************************************************/ + return AllocateBuffer(g); + } // end of OpenTableFile + +/****************************************************************************/ +/* Allocate the block buffer for the table. */ +/****************************************************************************/ +bool DBFFAM::AllocateBuffer(PGLOBAL g) + { + char c; + int rc; + MODE mode = Tdbp->GetMode(); + + Buflen = Blksize; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (mode == MODE_INSERT) { +#if defined(WIN32) + /************************************************************************/ + /* Now we can revert to binary mode in particular because the eventual */ + /* writing of a new header must be done in binary mode to avoid */ + /* translating 0A bytes (LF) into 0D0A (CRLF) by Windows in text mode. */ + /************************************************************************/ + if (_setmode(_fileno(Stream), _O_BINARY) == -1) { + sprintf(g->Message, MSG(BIN_MODE_FAIL), strerror(errno)); + return true; + } // endif setmode +#endif // WIN32 + + /************************************************************************/ + /* If this is a new file, the header must be generated. */ + /************************************************************************/ + int len = GetFileLength(g); + + if (!len) { + // Make the header for this DBF table file + struct tm *datm; + int hlen, n = 0, reclen = 1; + time_t t; + DBFHEADER *header; + DESCRIPTOR *descp; + PCOLDEF cdp; + PDOSDEF tdp = (PDOSDEF)Tdbp->GetDef(); + + // Count the number of columns + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) { + reclen += cdp->GetLong(); + n++; + } // endfor cdp + + if (Lrecl != reclen) { + sprintf(g->Message, MSG(BAD_LRECL), Lrecl, reclen); + return true; + } // endif Lrecl + + hlen = HEADLEN * (n + 1) + 2; + header = (DBFHEADER*)PlugSubAlloc(g, NULL, hlen); + memset(header, 0, hlen); + header->Version = DBFTYPE; + t = time(NULL) - (time_t)DTVAL::GetShift(); + datm = gmtime(&t); + header->Filedate[0] = datm->tm_year - 100; + header->Filedate[1] = datm->tm_mon + 1; + header->Filedate[2] = datm->tm_mday; + header->Headlen = (ushort)hlen; + header->Reclen = (ushort)reclen; + descp = (DESCRIPTOR*)header; + + // Currently only standard Xbase types are supported + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) { + descp++; + + switch ((c = *GetFormatType(cdp->GetType()))) { + case 'S': // Short integer + case 'L': // Large (big) integer + case 'T': // Tiny integer + c = 'N'; // Numeric + case 'N': // Numeric (integer) + case 'F': // Float (double) + descp->Decimals = (uchar)cdp->F.Prec; + case 'C': // Char + case 'D': // Date + break; + default: // Should never happen + sprintf(g->Message, "Unsupported DBF type %c for column %s", + c, cdp->GetName()); + return true; + } // endswitch c + + strncpy(descp->Name, cdp->GetName(), 11); + descp->Type = c; + descp->Length = (uchar)cdp->GetLong(); + } // endfor cdp + + *(char*)(++descp) = EOH; + + // Now write the header + if (fwrite(header, 1, hlen, Stream) != (unsigned)hlen) { + sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno)); + return true; + } // endif fwrite + + Records = 0; + Headlen = hlen; + } else if (len < 0) + return true; // Error in GetFileLength + + /************************************************************************/ + /* For Insert the buffer must be prepared. */ + /************************************************************************/ + memset(To_Buf, ' ', Buflen); + Rbuf = Nrec; // To be used by WriteDB + } else if (UseTemp) { + // Allocate a separate buffer so block reading can be kept + Dbflen = Nrec; + DelBuf = PlugSubAlloc(g, NULL, Blksize); + } // endif's + + if (!Headlen) { + /************************************************************************/ + /* Here is a good place to process the DBF file header */ + /************************************************************************/ + DBFHEADER header; + + if ((rc = dbfhead(g, Stream, Tdbp->GetFile(g), &header)) == RC_OK) { + if (Lrecl != (int)header.Reclen) { + sprintf(g->Message, MSG(BAD_LRECL), Lrecl, header.Reclen); + return true; + } // endif Lrecl + + Records = (int)header.Records; + Headlen = (int)header.Headlen; + } else if (rc == RC_NF) { + Records = 0; + Headlen = 0; + } else // RC_FX + return true; // Error in dbfhead + + } // endif Headlen + + /**************************************************************************/ + /* Position the file at the begining of the data. */ + /**************************************************************************/ + if (Tdbp->GetMode() == MODE_INSERT) + rc = fseek(Stream, 0, SEEK_END); + else + rc = fseek(Stream, Headlen, SEEK_SET); + + if (rc) { + sprintf(g->Message, MSG(BAD_DBF_FILE), Tdbp->GetFile(g)); + return true; + } // endif fseek + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Reset buffer access according to indexing and to mode. */ +/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ +/***********************************************************************/ +void DBFFAM::ResetBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* If access is random, performances can be much better when the */ + /* reads are done on only one row, except for small tables that can */ + /* be entirely read in one block. If the index is just used as a */ + /* bitmap filter, as for Update or delete, reading will be */ + /* sequential and we better keep block reading. */ + /*********************************************************************/ + if (Tdbp->GetKindex() && Tdbp->GetMode() == MODE_READ && + ReadBlks != 1) { + Nrec = 1; // Better for random access + Rbuf = 0; + Blksize = Lrecl; + OldBlk = -2; // Has no meaning anymore + Block = Tdbp->Cardinality(g); // Blocks are one line now + } // endif Mode + + } // end of ResetBuffer + +/***********************************************************************/ +/* ReadBuffer: Read one line for a DBF file. */ +/***********************************************************************/ +int DBFFAM::ReadBuffer(PGLOBAL g) + { + if (!Placed && !Closing && GetRowID() == Records) + return RC_EF; + + int rc = FIXFAM::ReadBuffer(g); + + if (rc != RC_OK || Closing) + return rc; + + switch (*Tdbp->GetLine()) { + case '*': + if (!ReadMode) + rc = RC_NF; // Deleted line + else + Rows++; + + break; + case ' ': + if (ReadMode < 2) + Rows++; // Non deleted line + else + rc = RC_NF; + + break; + default: + if (++Nerr >= Maxerr && !Accept) { + sprintf(g->Message, MSG(BAD_DBF_REC), Tdbp->GetFile(g), GetRowID()); + rc = RC_FX; + } else + rc = (Accept) ? RC_OK : RC_NF; + + } // endswitch To_Buf + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Copy the header into the temporary file. */ +/***********************************************************************/ +bool DBFFAM::CopyHeader(PGLOBAL g) + { + bool rc = true; + + if (Headlen) { + void *hdr = PlugSubAlloc(g, NULL, Headlen); + size_t n, hlen = (size_t)Headlen; + int pos = ftell(Stream); + + if (fseek(Stream, 0, SEEK_SET)) + strcpy(g->Message, "Seek error in CopyHeader"); + else if ((n = fread(hdr, 1, hlen, Stream)) != hlen) + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File); + else if ((n = fwrite(hdr, 1, hlen, T_Stream)) != hlen) + sprintf(g->Message, MSG(WRITE_STRERROR), To_Fbt->Fname + , strerror(errno)); + else if (fseek(Stream, pos, SEEK_SET)) + strcpy(g->Message, "Seek error in CopyHeader"); + else + rc = false; + + } else + rc = false; + + return rc; + } // end of CopyHeader + +/***********************************************************************/ +/* Data Base delete line routine for DBF access methods. */ +/* Deleted lines are just flagged in the first buffer character. */ +/***********************************************************************/ +int DBFFAM::DeleteRecords(PGLOBAL g, int irc) + { + if (irc == RC_OK) { + // T_Stream is the temporary stream or the table file stream itself + if (!T_Stream) + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + if (CopyHeader(g)) // For DBF tables + return RC_FX; + + } else + T_Stream = Stream; + + *Tdbp->GetLine() = '*'; + Modif++; // Modified line in Delete mode + } // endif irc + + return RC_OK; + } // end of DeleteRecords + +/***********************************************************************/ +/* Rewind routine for DBF access method. */ +/***********************************************************************/ +void DBFFAM::Rewind(void) + { + BLKFAM::Rewind(); + Nerr = 0; + } // end of Rewind + +/***********************************************************************/ +/* Table file close routine for DBF access method. */ +/***********************************************************************/ +void DBFFAM::CloseTableFile(PGLOBAL g) + { + int rc = RC_OK, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + // Closing is True if last Write was in error + if (mode == MODE_INSERT && CurNum && !Closing) { + // Some more inserted lines remain to be written + Rbuf = CurNum--; +// Closing = true; + wrc = WriteBuffer(g); + } else if (mode == MODE_UPDATE || mode == MODE_DELETE) { + if (Modif && !Closing) { + // Last updated block remains to be written + Closing = true; + wrc = ReadBuffer(g); + } // endif Modif + + if (UseTemp && T_Stream && wrc == RC_OK) { + // Copy any remaining lines + bool b; + + Fpos = Tdbp->Cardinality(g); + + if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) { + // Delete the old file and rename the new temp file. + RenameTempFile(g); + goto fin; + } // endif rc + + } // endif UseTemp + + } // endif's mode + + if (Tdbp->GetMode() == MODE_INSERT) { + int n = ftell(Stream) - Headlen; + + rc = PlugCloseFile(g, To_Fb); + + if (n >= 0 && !(n % Lrecl)) { + n /= Lrecl; // New number of lines + + if (n > Records) { + // Update the number of rows in the file header + char filename[_MAX_PATH]; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + if ((Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "r+b"))) + { + fseek(Stream, 4, SEEK_SET); // Get header.Records position + fwrite(&n, sizeof(int), 1, Stream); + fclose(Stream); + Stream= NULL; + Records= n; // Update Records value + } + } // endif n + + } // endif n + + } else // Finally close the file + rc = PlugCloseFile(g, To_Fb); + + fin: +#ifdef DEBTRACE + htrc("DBF CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n", + To_File, mode, wrc, rc); +#endif + Stream = NULL; // So we can know whether table is open + } // end of CloseTableFile + +/* ---------------------------- Class DBMFAM ------------------------------ */ + +/****************************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/****************************************************************************/ +int DBMFAM::Cardinality(PGLOBAL g) + { + if (!g) + return 1; + + if (!Headlen) + if ((Headlen = ScanHeader(g, To_File, Lrecl, Tdbp->GetPath())) < 0) + return -1; // Error in ScanHeader + + // Set number of blocks for later use + Block = (Records > 0) ? (Records + Nrec - 1) / Nrec : 0; + return Records; + } // end of Cardinality + +#if 0 // Not compatible with ROWID block optimization +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int DBMFAM::GetRowID(void) + { + return Rows; + } // end of GetRowID +#endif + +/***********************************************************************/ +/* Just check that on all deletion the unknown deleted line number is */ +/* sent back because Cardinality doesn't count soft deleted lines. */ +/***********************************************************************/ +int DBMFAM::GetDelRows(void) + { + if (Tdbp->GetMode() == MODE_DELETE && !Tdbp->GetNext()) + return -1; // Means all lines deleted + else + return DelRows; + + } // end of GetDelRows + +/****************************************************************************/ +/* Allocate the block buffer for the table. */ +/****************************************************************************/ +bool DBMFAM::AllocateBuffer(PGLOBAL g) + { + if (!Headlen) { + /************************************************************************/ + /* Here is a good place to process the DBF file header */ + /************************************************************************/ + DBFHEADER *hp = (DBFHEADER*)Memory; + + if (Lrecl != (int)hp->Reclen) { + sprintf(g->Message, MSG(BAD_LRECL), Lrecl, hp->Reclen); + return true; + } // endif Lrecl + + Records = (int)hp->Records; + Headlen = (int)hp->Headlen; + } // endif Headlen + + /**************************************************************************/ + /* Position the file at the begining of the data. */ + /**************************************************************************/ + Fpos = Mempos = Memory + Headlen; + Top--; // Because of EOF marker + return false; + } // end of AllocateBuffer + +/****************************************************************************/ +/* ReadBuffer: Read one line for a FIX file. */ +/****************************************************************************/ +int DBMFAM::ReadBuffer(PGLOBAL g) + { +// if (!Placed && GetRowID() == Records) +// return RC_EF; + + int rc = MPXFAM::ReadBuffer(g); + + if (rc != RC_OK) + return rc; + + switch (*Fpos) { + case '*': + if (!ReadMode) + rc = RC_NF; // Deleted line + else + Rows++; + + break; + case ' ': + if (ReadMode < 2) + Rows++; // Non deleted line + else + rc = RC_NF; + + break; + default: + if (++Nerr >= Maxerr && !Accept) { + sprintf(g->Message, MSG(BAD_DBF_REC), Tdbp->GetFile(g), GetRowID()); + rc = RC_FX; + } else + rc = (Accept) ? RC_OK : RC_NF; + } // endswitch To_Buf + + return rc; + } // end of ReadBuffer + +/****************************************************************************/ +/* Data Base delete line routine for DBF access methods. */ +/* Deleted lines are just flagged in the first buffer character. */ +/****************************************************************************/ +int DBMFAM::DeleteRecords(PGLOBAL g, int irc) + { + if (irc == RC_OK) + *Fpos = '*'; + + return RC_OK; + } // end of DeleteRecords + +/***********************************************************************/ +/* Rewind routine for DBF access method. */ +/***********************************************************************/ +void DBMFAM::Rewind(void) + { + MBKFAM::Rewind(); + Nerr = 0; + } // end of Rewind + +/* --------------------------------- EOF ---------------------------------- */ diff --git a/storage/connect/filamdbf.h b/storage/connect/filamdbf.h new file mode 100644 index 00000000000..d38e7f9b90f --- /dev/null +++ b/storage/connect/filamdbf.h @@ -0,0 +1,116 @@ +/***************** FilAmDbf H Declares Source Code File (.H) ****************/ +/* Name: filamdbf.h Version 1.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the DBF file access method classes declares. */ +/****************************************************************************/ + +#ifndef __FILAMDBF_H +#define __FILAMDBF_H + +#include "filamfix.h" +#include "filamap.h" + +typedef class DBFBASE *PDBF; +typedef class DBFFAM *PDBFFAM; +typedef class DBMFAM *PDBMFAM; + +/****************************************************************************/ +/* Functions used externally. */ +/****************************************************************************/ +PQRYRES DBFColumns(PGLOBAL g, char *fn, BOOL info); + +/****************************************************************************/ +/* This is the base class for dBASE file access methods. */ +/****************************************************************************/ +class DllExport DBFBASE { + public: + // Constructors + DBFBASE(PDOSDEF tdp); + DBFBASE(PDBF txfp); + + // Implementation + int ScanHeader(PGLOBAL g, PSZ fname, int lrecl, char *defpath); + + protected: + // Default constructor, not to be used + DBFBASE(void) {} + + // Members + int Records; /* records in the file */ + bool Accept; /* true if bad lines are accepted */ + int Nerr; /* Number of bad records */ + int Maxerr; /* Maximum number of bad records */ + int ReadMode; /* 1: ALL 2: DEL 0: NOT DEL */ +//PSZ Defpath; /* Default data path */ + }; // end of class DBFBASE + +/****************************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for DBase files. */ +/****************************************************************************/ +class DllExport DBFFAM : public FIXFAM, public DBFBASE { + public: + // Constructors + DBFFAM(PDOSDEF tdp) : FIXFAM(tdp), DBFBASE(tdp) {} + DBFFAM(PDBFFAM txfp) : FIXFAM(txfp), DBFBASE((PDBF)txfp) {} + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_DBF;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) DBFFAM(this);} + + // Methods + virtual int GetNerr(void) {return Nerr;} + virtual int Cardinality(PGLOBAL g); +//virtual int GetRowID(void); // Temporarily suppressed + virtual bool OpenTableFile(PGLOBAL g); + virtual bool AllocateBuffer(PGLOBAL g); + virtual void ResetBuffer(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); +//virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + // Members + virtual bool CopyHeader(PGLOBAL g); + +//int Records; in TXTFAM +//int Headlen; in TXTFAM + }; // end of class DBFFAM + +/****************************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for DBase files */ +/* using file mapping to access the file. */ +/****************************************************************************/ +class DllExport DBMFAM : public MPXFAM, public DBFBASE { + public: + // Constructors + DBMFAM(PDOSDEF tdp) : MPXFAM(tdp), DBFBASE(tdp) {} + DBMFAM(PDBMFAM txfp) : MPXFAM(txfp), DBFBASE((PDBF)txfp) {} + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_DBF;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) DBMFAM(this);} + virtual int GetDelRows(void); + + // Methods + virtual int GetNerr(void) {return Nerr;} + virtual int Cardinality(PGLOBAL g); +//virtual int GetRowID(void); // Temporarily suppressed + virtual bool AllocateBuffer(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); +//virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void Rewind(void); + + protected: + // Members +//int Records; in TXTFAM +//int Headlen; in TXTFAM + }; // end of class DBFFAM + +#endif // __FILAMDBF_H diff --git a/storage/connect/filamfix.cpp b/storage/connect/filamfix.cpp new file mode 100644 index 00000000000..b7cbc76a553 --- /dev/null +++ b/storage/connect/filamfix.cpp @@ -0,0 +1,1443 @@ +/*********** File AM Fix C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMFIX */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the FIX/BIN file access method classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <sys/stat.h> +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamfix.h is header containing the file AM classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "filamfix.h" +#include "tabdos.h" + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF +#endif + +extern int num_read, num_there, num_eq[2]; // Statistics + +/* --------------------------- Class FIXFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +FIXFAM::FIXFAM(PDOSDEF tdp) : BLKFAM(tdp) + { + Blksize = tdp->GetBlksize(); + Padded = tdp->GetPadded(); + + if (Padded && Blksize) + Nrec = Blksize / Lrecl; + else { + Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; + Blksize = Nrec * Lrecl; + Padded = false; + } // endelse + + } // end of FIXFAM standard constructor + +FIXFAM::FIXFAM(PFIXFAM txfp) : BLKFAM(txfp) + { + } // end of FIXFAM copy constructor + +/***********************************************************************/ +/* Allocate the block buffer for the table. */ +/***********************************************************************/ +bool FIXFAM::AllocateBuffer(PGLOBAL g) + { + Buflen = Blksize; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (UseTemp || Tdbp->GetMode() == MODE_DELETE) { + if (Padded) { + strcpy(g->Message, MSG(NO_MODE_PADDED)); + return true; + } // endif Padded + + // Allocate a separate buffer so block reading can be kept + Dbflen = Nrec; + DelBuf = PlugSubAlloc(g, NULL, Blksize); + } else if (Tdbp->GetMode() == MODE_INSERT) { + /*******************************************************************/ + /* For Insert the buffer must be prepared. */ + /*******************************************************************/ + memset(To_Buf, ' ', Buflen); + + if (/*Tdbp->GetFtype() < 2 &&*/ !Padded) + // If not binary, the file is physically a text file. + // We do it also for binary table because the lrecl can have been + // specified with additional space to include line ending. + for (int len = Lrecl; len <= Buflen; len += Lrecl) { +#if defined(WIN32) + To_Buf[len - 2] = '\r'; +#endif // WIN32 + To_Buf[len - 1] = '\n'; + } // endfor len + + Rbuf = Nrec; // To be used by WriteDB + } // endif Insert + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Reset buffer access according to indexing and to mode. */ +/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ +/***********************************************************************/ +void FIXFAM::ResetBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* If access is random, performances can be much better when the */ + /* reads are done on only one row, except for small tables that can */ + /* be entirely read in one block. If the index is just used as a */ + /* bitmap filter as for Update or Delete reading will be sequential */ + /* and we better keep block reading. */ + /*********************************************************************/ + if (Tdbp->GetMode() == MODE_READ && ReadBlks != 1 && !Padded && + Tdbp->GetKindex() /*&& Tdbp->GetKindex()->IsRandom()*/) { + Nrec = 1; // Better for random access + Rbuf = 0; + Blksize = Lrecl; + OldBlk = -2; // Has no meaning anymore + Block = Tdbp->Cardinality(g); // Blocks are one line now + } // endif Mode + + } // end of ResetBuffer + +/***********************************************************************/ +/* ReadBuffer: Read one line for a FIX file. */ +/***********************************************************************/ +int FIXFAM::ReadBuffer(PGLOBAL g) + { + int n, rc = RC_OK; + + if (!Closing) { + /*******************************************************************/ + /* Sequential reading when Placed is not true. */ + /*******************************************************************/ + if (Placed) { + Tdbp->SetLine(To_Buf + CurNum * Lrecl); + Placed = false; + } else if (++CurNum < Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; + } else if (Rbuf < Nrec && CurBlk != -1) { + return RC_EF; + } else { + /*****************************************************************/ + /* New block. */ + /*****************************************************************/ + CurNum = 0; + Tdbp->SetLine(To_Buf); + + if (++CurBlk >= Block) + return RC_EF; + + } // endif's + + if (OldBlk == CurBlk) { + IsRead = true; // Was read indeed + return RC_OK; // Block is already there + } // endif OldBlk + + } // endif !Closing + + if (Modif) { + /*******************************************************************/ + /* The old block was modified in Update mode. */ + /* In Update mode we simply rewrite the old block on itself. */ + /*******************************************************************/ + bool moved = false; + + if (UseTemp) // Copy any intermediate lines. + if (MoveIntermediateLines(g, &moved)) + rc = RC_FX; + + if (rc == RC_OK) { + // Fpos is last position, Headlen is DBF file header length + if (!moved && fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), 0); + rc = RC_FX; + } else if (fwrite(To_Buf, Lrecl, Rbuf, T_Stream) != (size_t)Rbuf) { + sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno)); + rc = RC_FX; + } // endif fwrite + + Spos = Fpos + Nrec; // + Rbuf ??? + } // endif rc + + if (Closing || rc != RC_OK) { // Error or called from CloseDB + Closing = true; // To tell CloseDB about error + return rc; + } // endif Closing + + // NOTE: Next line was added to avoid a very strange fread bug. + // When the fseek is not executed (even the file has the good + // pointer position) the next read can happen anywhere in the file. + OldBlk = CurBlk; // This will force fseek to be executed + Modif = 0; +// Spos = Fpos + Nrec; done above + } // endif Mode + + // This could be done only for new block. However note that FPOS + // is used as block position when updating and as line position + // when deleting so this has to be carefully checked. + Fpos = CurBlk * Nrec; // Fpos is new line position + + // fseek is required only in non sequential reading + if (CurBlk != OldBlk + 1) + // Note: Headlen is for DBF tables + if (fseek(Stream, Headlen + Fpos * Lrecl, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos); + return RC_FX; + } // endif fseek + +#ifdef DEBTRACE + htrc("File position is now %d\n", ftell(Stream)); +#endif + +//long tell = ftell(Stream); not used + + if (Padded) + n = fread(To_Buf, (size_t)Blksize, 1, Stream); + else + n = fread(To_Buf, (size_t)Lrecl, (size_t)Nrec, Stream); + + if (n) { + rc = RC_OK; + Rbuf = (Padded) ? n * Nrec : n; + ReadBlks++; + num_read++; + } else if (feof(Stream)) { + rc = RC_EF; + } else { +#if defined(UNIX) + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno)); +#else + sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL)); +#endif + +#ifdef DEBTRACE + htrc("%s\n", g->Message); +#endif + return RC_FX; + } // endelse + + OldBlk = CurBlk; // Last block actually read + IsRead = true; // Is read indeed + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for FIX access method. */ +/* Updates are written into the (Temp) file in ReadBuffer. */ +/***********************************************************************/ +int FIXFAM::WriteBuffer(PGLOBAL g) + { +#ifdef DEBTRACE + fprintf(debug, + "FIX WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n", + Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum); +#endif + + if (Tdbp->GetMode() == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode, blocs are added sequentialy to the file end. */ + /*******************************************************************/ + if (++CurNum != Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; // We write only full blocks + } // endif CurNum + +#ifdef DEBTRACE + htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf); +#endif + + // Now start the writing process. + if (fwrite(To_Buf, Lrecl, Rbuf, Stream) != (size_t)Rbuf) { + sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno)); + Closing = true; // To tell CloseDB about a Write error + return RC_FX; + } // endif size + + CurBlk++; + CurNum = 0; + Tdbp->SetLine(To_Buf); + +#ifdef DEBTRACE + htrc("write done\n"); +#endif + + } else { // Mode == MODE_UPDATE + // T_Stream is the temporary stream or the table file stream itself + if (!T_Stream) + { + if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) { + if (OpenTempFile(g)) + return RC_FX; + + if (CopyHeader(g)) // For DBF tables + return RC_FX; + + } else + T_Stream = Stream; + } + Modif++; // Modified line in Update mode + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for FIXFAM access method. */ +/***********************************************************************/ +int FIXFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool moved; + + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This will be experimented. */ + /*********************************************************************/ +#ifdef DEBTRACE + fprintf(debug, + "DOS DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); +#endif + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = Tdbp->Cardinality(g); +#ifdef DEBTRACE + htrc("Fpos placed at file end=%d\n", Fpos); +#endif + } else // Fpos is the deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + /*******************************************************************/ + /* First line to delete. */ + /*******************************************************************/ + if (UseTemp) { + /*****************************************************************/ + /* Open temporary file, lines before this will be moved. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block moving. */ + /*****************************************************************/ + T_Stream = Stream; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &moved)) + return RC_FX; + + if (irc == RC_OK) { + /*******************************************************************/ + /* Reposition the file pointer and set Spos. */ + /*******************************************************************/ + Spos = Fpos + 1; // New start position is on next line + + if (moved) { + if (fseek(Stream, Spos * Lrecl, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), 0); + return RC_FX; + } // endif fseek + + OldBlk = -2; // To force fseek to be executed on next block + } // endif moved + +#ifdef DEBTRACE + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); +#endif + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + if (UseTemp) { + /*****************************************************************/ + /* Ok, now delete old file and rename new temp file. */ + /*****************************************************************/ + if (RenameTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS ??) this is still to be checked */ + /* for compatibility with Text files and other OS's. */ + /*****************************************************************/ + char filename[_MAX_PATH]; + int rc, h; + + rc = PlugCloseFile(g, To_Fb); + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Tpos * Lrecl))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Tpos * Lrecl)) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + +#ifdef DEBTRACE + htrc("done, h=%d irc=%d\n", h, irc); +#endif + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/* This works only for file open in binary mode. */ +/***********************************************************************/ +bool FIXFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int n; + size_t req, len; + + for (*b = false, n = Fpos - Spos; n > 0; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!UseTemp || !*b) + if (fseek(Stream, Headlen + Spos * Lrecl, SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + req = (size_t)min(n, Dbflen); + len = fread(DelBuf, Lrecl, req, Stream); + +#ifdef DEBTRACE + htrc("after read req=%d len=%d\n", req, len); +#endif + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp) // Delete mode, cannot be a DBF file + if (fseek(T_Stream, Tpos * Lrecl, SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(DelBuf, Lrecl, req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + +#ifdef DEBTRACE + htrc("after write pos=%d\n", ftell(Stream)); +#endif + + Tpos += (int)req; + Spos += (int)req; + +#ifdef DEBTRACE + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); +#endif + + *b = true; + } // endfor n + + return false; + } // end of MoveIntermediate Lines + +/***********************************************************************/ +/* Table file close routine for FIX access method. */ +/***********************************************************************/ +void FIXFAM::CloseTableFile(PGLOBAL g) + { + int rc = RC_OK, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + // Closing is True if last Write was in error + if (mode == MODE_INSERT && CurNum && !Closing) { + // Some more inserted lines remain to be written + Rbuf = CurNum--; +// Closing = true; + wrc = WriteBuffer(g); + } else if (mode == MODE_UPDATE) { + if (Modif && !Closing) { + // Last updated block remains to be written + Closing = true; + wrc = ReadBuffer(g); + } // endif Modif + + if (UseTemp && T_Stream && wrc == RC_OK) { + // Copy any remaining lines + bool b; + + Fpos = Tdbp->Cardinality(g); + + if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) { + // Delete the old file and rename the new temp file. + RenameTempFile(g); + goto fin; + } // endif rc + + } // endif UseTemp + + } // endif's mode + + // Finally close the file + rc = PlugCloseFile(g, To_Fb); + + fin: +#ifdef DEBTRACE + htrc("FIX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n", + To_File, mode, wrc, rc); +#endif + Stream = NULL; // So we can know whether table is open + } // end of CloseTableFile + +/* ------------------------- Class BGXFAM ---------------------------- */ + +/***********************************************************************/ +/* Implementation of the BGXFAM class. */ +/* This is the FAM class for FIX tables of more than 2 gigabytes. */ +/***********************************************************************/ +BGXFAM::BGXFAM(PDOSDEF tdp) : FIXFAM(tdp) + { + Hfile = INVALID_HANDLE_VALUE; + Tfile = INVALID_HANDLE_VALUE; + } // end of BGXFAM constructor + +BGXFAM::BGXFAM(PBGXFAM txfp) : FIXFAM(txfp) + { + Hfile = txfp->Hfile; + Tfile = txfp->Tfile; + } // end of BGXFAM copy constructor + +/***********************************************************************/ +/* Set current position in a big file. */ +/***********************************************************************/ +bool BGXFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, int org) + { +#if defined(WIN32) + char buf[256]; + DWORD drc; + LARGE_INTEGER of; + + of.QuadPart = pos; + of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, org); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (drc = GetLastError()) != NO_ERROR) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(SFP_ERROR), buf); + return true; + } // endif +#else // !WIN32 + if (lseek64(h, pos, org) < 0) { + sprintf(g->Message, MSG(ERROR_IN_LSK), errno); + return true; + } // endif +#endif // !WIN32 + + return false; + } // end of BigSeek + +/***********************************************************************/ +/* Read from a big file. */ +/***********************************************************************/ +int BGXFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + int rc; + +#if defined(WIN32) + DWORD nbr, drc, len = (DWORD)req; + bool brc = ReadFile(h, inbuf, len, &nbr, NULL); + +#ifdef DEBTRACE + htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr); +#endif + + if (!brc) { + char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile"; + + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(READ_ERROR), To_File, buf); +#ifdef DEBTRACE + htrc("BIGREAD: %s\n", g->Message); +#endif + rc = -1; + } else + rc = (int)nbr; +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbr = read(h, inbuf, len); + + rc = (int)nbr; +#endif // !WIN32 + + return rc; + } // end of BigRead + +/***********************************************************************/ +/* Write into a big file. */ +/***********************************************************************/ +bool BGXFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbw, drc, len = (DWORD)req; + bool brc = WriteFile(h, inbuf, len, &nbw, NULL); + +#ifdef DEBTRACE + htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw); +#endif + + if (!brc || nbw != len) { + char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_NUM)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf); + +#ifdef DEBTRACE + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, drc, g->Message); +#endif + rc = true; + } // endif brc || nbw +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbw = write(h, inbuf, len); + + if (nbw != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); +#ifdef DEBTRACE + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, errno, g->Message); +#endif + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigWrite + +#if 0 +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void BGXFAM::Reset(void) + { + FIXFAM::Reset(); + Xpos = 0; + } // end of Reset +#endif // 0 + +/***********************************************************************/ +/* OpenTableFile: opens a huge file using Windows/Unix API's. */ +/***********************************************************************/ +bool BGXFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) { + sprintf(g->Message, MSG(FILE_OPEN_YET), To_File); + return true; + } // endif + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + +#ifdef DEBTRACE + htrc("OpenTableFile: filename=%s mode=%d\n", filename, mode); +#endif + +#if defined(WIN32) + DWORD rc, access, creation, share = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + creation = OPEN_EXISTING; + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted rows + DelRows = Cardinality(g); + + // This will delete the whole file and provoque ReadDB to + // return immediately. + access = GENERIC_READ | GENERIC_WRITE; + creation = TRUNCATE_EXISTING; + Tdbp->ResetSize(); + Headlen = 0; + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + if ((UseTemp = Tdbp->IsUsingTemp(g))) + access = GENERIC_READ; + else + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_EXISTING; + break; + case MODE_INSERT: + access = GENERIC_WRITE; + creation = OPEN_ALWAYS; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + Hfile = CreateFile(filename, access, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + } else + rc = 0; + +#ifdef DEBTRACE + fprintf(debug, + " rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n", + rc, access, share, creation, Hfile, filename); +#endif + + if (mode == MODE_INSERT) + /*******************************************************************/ + /* In Insert mode we must position the cursor at end of file. */ + /*******************************************************************/ + if (BigSeek(g, Hfile, (BIGINT)0, FILE_END)) + return true; + +#else // UNIX + int rc = 0; + int oflag = O_LARGEFILE; // Enable file size > 2G + mode_t tmode = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + oflag |= O_RDONLY; + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // This will delete the whole file and provoque ReadDB to + // return immediately. + oflag |= (O_RDWR | O_TRUNC); + Tdbp->ResetSize(); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + oflag |= (UseTemp) ? O_RDONLY : O_RDWR; + break; + case MODE_INSERT: + oflag |= (O_WRONLY | O_CREAT | O_APPEND); + tmode = S_IREAD | S_IWRITE; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, tmode); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = errno; + } else + rc = 0; + +#ifdef DEBTRACE + htrc(" rc=%d oflag=%p tmode=%p handle=%p fn=%s\n", + rc, oflag, tmode, Hfile, filename); +#endif + +#endif // UNIX + + if (!rc) { + if (!To_Fb) { + To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fb->Fname = To_File; + To_Fb->Type = TYPE_FB_HANDLE; + To_Fb->Memory = NULL; + To_Fb->Length = 0; + To_Fb->Mode = mode; + To_Fb->File = NULL; + To_Fb->Next = dbuserp->Openlist; + dbuserp->Openlist = To_Fb; + } // endif To_Fb + + To_Fb->Count = 1; + To_Fb->Mode = mode; + To_Fb->Handle = Hfile; + + /*******************************************************************/ + /* Allocate the block buffer. */ + /*******************************************************************/ + return AllocateBuffer(g); + } else + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + + } // end of OpenTableFile + +/***********************************************************************/ +/* BIGFIX Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int BGXFAM::Cardinality(PGLOBAL g) + { + if (g) { + char filename[_MAX_PATH]; + int card = -1; + BIGINT fsize; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + +#if defined(WIN32) // OB + LARGE_INTEGER len; + DWORD rc = 0; + + len.QuadPart = -1; + + if (Hfile == INVALID_HANDLE_VALUE) { + HANDLE h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + if ((rc = GetLastError()) != ERROR_FILE_NOT_FOUND) { + sprintf(g->Message, MSG(OPEN_ERROR), rc, 10, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + return -1; + } else + return 0; // File does not exist + + // Get the size of the file (can be greater than 4 GB) + len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart); + CloseHandle(h); + } else + len.LowPart = GetFileSize(Hfile, (LPDWORD)&len.HighPart); + + if (len.LowPart == 0xFFFFFFFF && (rc = GetLastError()) != NO_ERROR) { + sprintf(g->Message, MSG(FILELEN_ERROR), "GetFileSize", filename); + return -2; + } else + fsize = len.QuadPart; + +#else // UNIX + if (Hfile == INVALID_HANDLE_VALUE) { + int h = open64(filename, O_RDONLY, 0); + +#ifdef DEBTRACE + htrc(" h=%d\n", h); +#endif + + if (h == INVALID_HANDLE_VALUE) { +#ifdef DEBTRACE + htrc(" errno=%d ENOENT=%d\n", errno, ENOENT); +#endif + if (errno != ENOENT) { + sprintf(g->Message, MSG(OPEN_ERROR_IS), + filename, strerror(errno)); + return -1; + } else + return 0; // File does not exist + + } // endif h + + // Get the size of the file (can be greater than 4 GB) + fsize = lseek64(h, 0, SEEK_END); + close(h); + } else { + BIGINT curpos = lseek64(Hfile, 0, SEEK_CUR); + + fsize = lseek64(Hfile, 0, SEEK_END); + lseek64(Hfile, curpos, SEEK_SET); + } // endif Hfile + + if (fsize < 0) { + sprintf(g->Message, MSG(FILELEN_ERROR), "lseek64", filename); + return -2; + } // endif fsize + +#endif // UNIX + + // Check the real size of the file + if (Padded && Blksize) { + if (fsize % (BIGINT)Blksize) { + sprintf(g->Message, MSG(NOT_FIXED_LEN), + filename, (int)fsize, Lrecl); + return -3; + } else + card = (int)(fsize / (BIGINT)Blksize) * Nrec; + + } else if (fsize % (BIGINT)Lrecl) { + sprintf(g->Message, MSG(NOT_FIXED_LEN), filename, (int)fsize, Lrecl); + return -3; + } else + card = (int)(fsize / (BIGINT)Lrecl); // Fixed length file + +#ifdef DEBTRACE + htrc(" Computed max_K=%d fsize=%lf lrecl=%d\n", + card, (double)fsize, Lrecl); +#endif + + // Set number of blocks for later use + Block = (card + Nrec - 1) / Nrec; + return card; + } else + return -1; + + } // end of Cardinality + +/***********************************************************************/ +/* ReadBuffer: Read Nrec lines for a big fixed/binary file. */ +/***********************************************************************/ +int BGXFAM::ReadBuffer(PGLOBAL g) + { + int nbr, rc = RC_OK; + + if (!Closing) { + /*******************************************************************/ + /* Sequential reading when Placed is not true. */ + /*******************************************************************/ + if (Placed) { + Tdbp->SetLine(To_Buf + CurNum * Lrecl); + Placed = false; + } else if (++CurNum < Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; + } else if (Rbuf < Nrec && CurBlk != -1) { + return RC_EF; + } else { + /*****************************************************************/ + /* New block. */ + /*****************************************************************/ + CurNum = 0; + Tdbp->SetLine(To_Buf); + + if (++CurBlk >= Block) + return RC_EF; + + } // endif's + + if (OldBlk == CurBlk) { + IsRead = true; // Was read indeed + return RC_OK; // Block is already there + } // endif OldBlk + + } // endif !Closing + + if (Modif) { + /*******************************************************************/ + /* The old block was modified in Update mode. */ + /* In Update mode we simply rewrite the old block on itself. */ + /*******************************************************************/ + bool moved = false; + + if (UseTemp) // Copy any intermediate lines. + if (MoveIntermediateLines(g, &moved)) + rc = RC_FX; + + if (rc == RC_OK) { + // Set file position to OldBlk position (Fpos) + if (!moved && BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl)) + rc = RC_FX; + else if (BigWrite(g, Tfile, To_Buf, Lrecl * Rbuf)) + rc = RC_FX; + + Spos = Fpos + Nrec; // + Rbuf ??? + } // endif rc + + if (Closing || rc != RC_OK) // Error or called from CloseDB + return rc; + + // NOTE: Next line was added to avoid a very strange fread bug. + // When the fseek is not executed (even the file has the good + // pointer position) the next read can happen anywhere in the file. + OldBlk = CurBlk; // This will force fseek to be executed + Modif = 0; + } // endif Mode + + Fpos = CurBlk * Nrec; + + // Setting file pointer is required only in non sequential reading + if (CurBlk != OldBlk + 1) + if (BigSeek(g, Hfile, (BIGINT)Fpos * (BIGINT)Lrecl)) + return RC_FX; + +#ifdef DEBTRACE + htrc("File position is now %d\n", Fpos); +#endif + + nbr = BigRead(g, Hfile, To_Buf, (Padded) ? Blksize : Lrecl * Nrec); + + if (nbr > 0) { + Rbuf = (Padded) ? Nrec : nbr / Lrecl; + rc = RC_OK; + ReadBlks++; + num_read++; + } else + rc = (nbr == 0) ? RC_EF : RC_FX; + + OldBlk = CurBlk; // Last block actually read + IsRead = true; // Is read indeed + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for BGXFAM access method. */ +/* Updates are written into the (Temp) file in ReadBuffer. */ +/***********************************************************************/ +int BGXFAM::WriteBuffer(PGLOBAL g) + { +#ifdef DEBTRACE + fprintf(debug, + "BIG WriteDB: Mode=%d buf=%p line=%p Nrec=%d Rbuf=%d CurNum=%d\n", + Tdbp->GetMode(), To_Buf, Tdbp->GetLine(), Nrec, Rbuf, CurNum); +#endif + + if (Tdbp->GetMode() == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode, blocks are added sequentialy to the file end. */ + /*******************************************************************/ + if (++CurNum != Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; // We write only full blocks + } // endif CurNum + +#ifdef DEBTRACE + htrc(" First line is '%.*s'\n", Lrecl - 2, To_Buf); +#endif + + // Now start the writing process. + if (BigWrite(g, Hfile, To_Buf, Lrecl * Rbuf)) + return RC_FX; + + CurBlk++; + CurNum = 0; + Tdbp->SetLine(To_Buf); + +#ifdef DEBTRACE + htrc("write done\n"); +#endif + + } else { // Mode == MODE_UPDATE + // Tfile is the temporary file or the table file handle itself + if (Tfile == INVALID_HANDLE_VALUE) + { + if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) { + if (OpenTempFile(g)) + return RC_FX; + + } else + Tfile = Hfile; + } + Modif++; // Modified line in Update mode + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for BGXFAM access method. */ +/***********************************************************************/ +int BGXFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool moved; + + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This will be experimented. */ + /*********************************************************************/ +#ifdef DEBTRACE + fprintf(debug, + "BGX DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); +#endif + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = Tdbp->Cardinality(g); +#ifdef DEBTRACE + htrc("Fpos placed at file end=%d\n", Fpos); +#endif + } else // Fpos is the deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here if a temporary file is not used, just the */ + /* setting of future Spos and Tpos. */ + /*******************************************************************/ + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Tfile = Hfile; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &moved)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef DEBTRACE + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (moved) { + if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl)) + return RC_FX; + + OldBlk = -2; // To force fseek to be executed on next block + } // endif moved + +#ifdef DEBTRACE + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); +#endif + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + char filename[_MAX_PATH]; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (UseTemp) { + /*****************************************************************/ + /* Ok, now delete old file and rename new temp file. */ + /*****************************************************************/ + if (RenameTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ +#if defined(WIN32) + if (BigSeek(g, Hfile, (BIGINT)Tpos * (BIGINT)Lrecl)) + return RC_FX; + + if (!SetEndOfFile(Hfile)) { + DWORD drc = GetLastError(); + + sprintf(g->Message, MSG(SETEOF_ERROR), drc); + return RC_FX; + } // endif error +#else // !WIN32 + if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + return RC_FX; + } // endif +#endif // !WIN32 + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool BGXFAM::OpenTempFile(PGLOBAL g) + { + char *tempname; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + remove(tempname); // Be sure it does not exist yet + +#if defined(WIN32) + Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Tfile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)tempname, _MAX_PATH, NULL); + strcat(g->Message, tempname); + return true; + } // endif Tfile +#else // UNIX + Tfile = open64(tempname, O_WRONLY | O_TRUNC, S_IWRITE); + + if (Tfile == INVALID_HANDLE_VALUE) { + int rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); + strcat(g->Message, strerror(errno)); + return true; + } //endif Tfile +#endif // UNIX + + To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fbt->Fname = tempname; + To_Fbt->Type = TYPE_FB_HANDLE; + To_Fbt->Memory = NULL; + To_Fbt->Length = 0; + To_Fbt->File = NULL; + To_Fbt->Next = dup->Openlist; + To_Fbt->Count = 1; + To_Fbt->Mode = MODE_INSERT; + To_Fbt->Handle = Tfile; + dup->Openlist = To_Fbt; + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool BGXFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int n, req, nbr; + + for (*b = false, n = Fpos - Spos; n > 0; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!UseTemp || !*b) + if (BigSeek(g, Hfile, (BIGINT)Spos * (BIGINT)Lrecl)) + return true; + + req = min(n, Dbflen) * Lrecl; + + if ((nbr = BigRead(g, Hfile, DelBuf, req)) != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), req, nbr); + return true; + } // endif nbr + + if (!UseTemp) + if (BigSeek(g, Tfile, (BIGINT)Tpos * (BIGINT)Lrecl)) + return true; + + if (BigWrite(g, Tfile, DelBuf, req)) + return true; + + req /= Lrecl; + Tpos += (int)req; + Spos += (int)req; + +#ifdef DEBTRACE + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); +#endif + + *b = true; + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Data Base close routine for BIGFIX access method. */ +/***********************************************************************/ +void BGXFAM::CloseTableFile(PGLOBAL g) + { + int rc = RC_OK, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + // Closing is True if last Write was in error + if (mode == MODE_INSERT && CurNum && !Closing) { + // Some more inserted lines remain to be written + Rbuf = CurNum--; + wrc = WriteBuffer(g); + } else if (mode == MODE_UPDATE) { + if (Modif && !Closing) { + // Last updated block remains to be written + Closing = true; + wrc = ReadBuffer(g); + } // endif Modif + + if (UseTemp && Tfile && wrc == RC_OK) { + // Copy any remaining lines + bool b; + + Fpos = Tdbp->Cardinality(g); + + if ((rc = MoveIntermediateLines(g, &b)) == RC_OK) { + // Delete the old file and rename the new temp file. + RenameTempFile(g); + goto fin; + } // endif rc + + } // endif UseTemp + + } // endif's mode + + // Finally close the file + rc = PlugCloseFile(g, To_Fb); + + fin: +#ifdef DEBTRACE + htrc("BGX CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n", + To_File, mode, wrc, rc); +#endif + Hfile = INVALID_HANDLE_VALUE; // So we can know whether table is open + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for huge FIX access method. */ +/* Note: commenting out OldBlk = -1 has two advantages: */ +/* 1 - It forces fseek on first block, thus suppressing the need to */ +/* rewind the file, anyway unuseful when second pass if indexed. */ +/* 2 - It permit to avoid re-reading small tables having only 1 block.*/ +/* (even very unlikely for huge files!) */ +/***********************************************************************/ +void BGXFAM::Rewind(void) + { +#if 0 // This is probably unuseful because file is accessed directly +#if defined(WIN32) //OB + SetFilePointer(Hfile, 0, NULL, FILE_BEGIN); +#else // UNIX + lseek64(Hfile, 0, SEEK_SET); +#endif // UNIX +#endif // 0 + CurBlk = -1; + CurNum = Rbuf; +//OldBlk = -1; +//Rbuf = 0; commented out in case we reuse last read block + Fpos = 0; + } // end of Rewind diff --git a/storage/connect/filamfix.h b/storage/connect/filamfix.h new file mode 100644 index 00000000000..758d891bf2c --- /dev/null +++ b/storage/connect/filamfix.h @@ -0,0 +1,90 @@ +/************** FilAMFix H Declares Source Code File (.H) **************/ +/* Name: FILAMFIX.H Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005 - 2012 */ +/* */ +/* This file contains the FIX file access method classes declares. */ +/***********************************************************************/ + +#ifndef __FILAMFIX_H +#define __FILAMFIX_H + +#include "filamtxt.h" + +typedef class FIXFAM *PFIXFAM; +typedef class BGXFAM *PBGXFAM; + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for standard */ +/* files with fixed record format (FIX, BIN) */ +/***********************************************************************/ +class DllExport FIXFAM : public BLKFAM { + public: + // Constructor + FIXFAM(PDOSDEF tdp); + FIXFAM(PFIXFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_FIX;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) FIXFAM(this);} + + // Methods + virtual int Cardinality(PGLOBAL g) {return TXTFAM::Cardinality(g);} + virtual int MaxBlkSize(PGLOBAL g, int s) + {return TXTFAM::MaxBlkSize(g, s);} + virtual bool AllocateBuffer(PGLOBAL g); + virtual void ResetBuffer(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + + protected: + virtual bool CopyHeader(PGLOBAL g) {return false;} + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b); + + // No additional members + }; // end of class FIXFAM + + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* that are standard files with columns starting at fixed offset */ +/* This class is for fixed formatted files of more than 2 gigabytes. */ +/***********************************************************************/ +class BGXFAM : public FIXFAM { + public: + // Constructor + BGXFAM(PDOSDEF tdp); + BGXFAM(PBGXFAM txfp); + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) BGXFAM(this);} + + // Methods +//virtual void Reset(void); + virtual int Cardinality(PGLOBAL g); + virtual bool OpenTableFile(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos + , int org = FILE_BEGIN); + int BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req); + bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req); + virtual bool OpenTempFile(PGLOBAL g); + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL); + + // Members + HANDLE Hfile; // Handle(descriptor) to big file + HANDLE Tfile; // Handle(descriptor) to big temp file +//BIGINT Xpos; // Current file position + }; // end of class BGXFAM + +#endif // __FILAMFIX_H diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp new file mode 100644 index 00000000000..ed4467b6392 --- /dev/null +++ b/storage/connect/filamtxt.cpp @@ -0,0 +1,1408 @@ +/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMTXT */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the Text file access method classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) || defined(UNIV_LINUX) +#include <errno.h> +#include <unistd.h> +//#if !defined(sun) // Sun has the ftruncate fnc. +//#define USETEMP // Force copy mode for DELETE +//#endif // !sun +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "filamtxt.h" +#include "tabdos.h" + +#if defined(UNIX) || defined(UNIV_LINUX) +#include "osutil.h" +#define _fileno fileno +#define _O_RDONLY O_RDONLY +#endif + +extern int num_read, num_there, num_eq[2]; // Statistics +extern "C" int trace; + +/* --------------------------- Class TXTFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +TXTFAM::TXTFAM(PDOSDEF tdp) + { + Tdbp = NULL; + To_Fb = NULL; + To_File = tdp->Fn; + Lrecl = tdp->Lrecl; + Placed = false; + IsRead = true; + Blocked = false; + To_Buf = NULL; + DelBuf = NULL; + BlkPos = NULL; + BlkLen = 0; + Buflen = 0; + Dbflen = 0; + Rows = 0; + DelRows = 0; + Headlen = 0; + Block = 0; + Last = 0; + Nrec = 1; + OldBlk = -1; + CurBlk = -1; + ReadBlks = 0; + CurNum = 0; + Rbuf = 0; + Modif = 0; + Blksize = 0; + Padded = false; + Eof = tdp->Eof; + Ending = tdp->Ending; + CrLf = (char*)(Ending == 2 ? "\r\n" : "\n"); + } // end of TXTFAM standard constructor + +TXTFAM::TXTFAM(PTXF txfp) + { + Tdbp = txfp->Tdbp; + To_Fb = txfp->To_Fb; + To_File = txfp->To_File; + Lrecl = txfp->Lrecl; + Placed = txfp->Placed; + IsRead = txfp->IsRead; + Blocked = txfp->Blocked; + To_Buf = txfp->To_Buf; + DelBuf = txfp->DelBuf; + BlkPos = txfp->BlkPos; + BlkLen = txfp->BlkLen; + Buflen = txfp->Buflen; + Dbflen = txfp->Dbflen; + Rows = txfp->Rows; + DelRows = txfp->DelRows; + Headlen = txfp->Headlen; + Block = txfp->Block; + Last = txfp->Last; + Nrec = txfp->Nrec; + OldBlk = txfp->OldBlk; + CurBlk = txfp->CurBlk; + ReadBlks = txfp->ReadBlks; + CurNum = txfp->CurNum; + Rbuf = txfp->Rbuf; + Modif = txfp->Modif; + Blksize = txfp->Blksize; + Padded = txfp->Padded; + Eof = txfp->Eof; + Ending = txfp->Ending; + } // end of TXTFAM copy constructor + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void TXTFAM::Reset(void) + { + Rows = 0; + DelRows = 0; + OldBlk = -1; + CurBlk = -1; + ReadBlks = 0; + CurNum = 0; + Rbuf = 0; + Modif = 0; + Placed = false; + } // end of Reset + +/***********************************************************************/ +/* TXT GetFileLength: returns file size in number of bytes. */ +/***********************************************************************/ +int TXTFAM::GetFileLength(PGLOBAL g) + { + char filename[_MAX_PATH]; + int h; + int len; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY); + + if (trace) + htrc("GetFileLength: fn=%s h=%d\n", filename, h); + + if (h == -1) { + if (errno != ENOENT) { + if (trace) + htrc("%s\n", g->Message); + len = -1; + } + else + { + len = 0; // File does not exist yet + g->Message[0]= '\0'; + } + } else { + if ((len = _filelength(h)) < 0) + sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename); + + if (Eof && len) + len--; // Do not count the EOF character + + close(h); + } // endif h + + return len; + } // end of GetFileLength + +/***********************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/* Note: This function is meant only for fixed length files but is */ +/* placed here to be available to FIXFAM and MPXFAM classes. */ +/***********************************************************************/ +int TXTFAM::Cardinality(PGLOBAL g) + { + if (g) { + int card = -1; + int len = GetFileLength(g); + + if (len >= 0) { + if (Padded && Blksize) { + if (!(len % Blksize)) + card = (len / Blksize) * Nrec; + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl); + + } else { + if (!(len % Lrecl)) + card = len / (int)Lrecl; // Fixed length file + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl); + + } // endif Padded + + if (trace) + htrc(" Computed max_K=%d Filen=%d lrecl=%d\n", + card, len, Lrecl); + + } else + card = 0; + + // Set number of blocks for later use + Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; + return card; + } else + return 1; + + } // end of Cardinality + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/* Note: This function is meant only for fixed length files but is */ +/* placed here to be available to FIXFAM and MPXFAM classes. */ +/***********************************************************************/ +int TXTFAM::MaxBlkSize(PGLOBAL g, int s) + { + int savcur = CurBlk, blm1 = Block - 1; + int size, last = s - blm1 * Nrec; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + size += (CurBlk == blm1) ? last : Nrec; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/* --------------------------- Class DOSFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp) + { + To_Fbt = NULL; + Stream = NULL; + T_Stream = NULL; + Fpos = Spos = Tpos = 0; + UseTemp = false; + Bin = false; + } // end of DOSFAM standard constructor + +DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp) + { + To_Fbt = tdfp->To_Fbt; + Stream = tdfp->Stream; + T_Stream = tdfp->T_Stream; + Fpos = tdfp->Fpos; + Spos = tdfp->Spos; + Tpos = tdfp->Tpos; + UseTemp = tdfp->UseTemp; + Bin = tdfp->Bin; + } // end of DOSFAM copy constructor + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void DOSFAM::Reset(void) + { + TXTFAM::Reset(); + Bin = false; + Fpos = Tpos = Spos = 0; + } // end of Reset + +/***********************************************************************/ +/* DOS GetFileLength: returns file size in number of bytes. */ +/***********************************************************************/ +int DOSFAM::GetFileLength(PGLOBAL g) + { + int len; + + if (!Stream) + len = TXTFAM::GetFileLength(g); + else + if ((len = _filelength(_fileno(Stream))) < 0) + sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File); + + if (trace) + htrc("File length=%d\n", len); + + return len; + } // end of GetFileLength + +/***********************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int DOSFAM::Cardinality(PGLOBAL g) + { + return (g) ? -1 : 0; + } // end of Cardinality + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/* Note: This function is not really implemented yet. */ +/***********************************************************************/ +int DOSFAM::MaxBlkSize(PGLOBAL g, int s) + { + return s; + } // end of MaxBlkSize + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */ +/***********************************************************************/ +bool DOSFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; +//int ftype = Tdbp->GetFtype(); + MODE mode = Tdbp->Mode; + PDBUSER dbuserp = PlgGetUser(g); + + // This is required when using Unix files under Windows + Bin = (Ending == 1); + + switch (mode) { + case MODE_READ: + strcpy(opmode, "r"); + break; + case MODE_DELETE: + if (!Tdbp->Next) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + if (Blocked) { + // Cardinality must return 0 + Block = 0; + Last = Nrec; + } // endif blocked + + // This will erase the entire file + strcpy(opmode, "w"); + Tdbp->ResetSize(); + break; + } // endif + + // Selective delete, pass thru + Bin = true; + case MODE_UPDATE: + if ((UseTemp = Tdbp->IsUsingTemp(g))) { + strcpy(opmode, "r"); + Bin = true; + } else + strcpy(opmode, "r+"); + + break; + case MODE_INSERT: + strcpy(opmode, "a+"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + // For blocked I/O or for moving lines, open the table in binary + strcat(opmode, (Blocked || Bin) ? "b" : "t"); + + // Now open the file stream + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Stream + + if (trace) + htrc("File %s open Stream=%p mode=%s\n", filename, Stream, opmode); + + To_Fb = dbuserp->Openlist; // Keep track of File block + + /*********************************************************************/ + /* Allocate the line buffer. For mode Delete a bigger buffer has to */ + /* be allocated because is it also used to move lines into the file.*/ + /*********************************************************************/ + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the line buffer. For mode Delete a bigger buffer has to */ +/* be allocated because is it also used to move lines into the file. */ +/***********************************************************************/ +bool DOSFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->Mode; + + // Lrecl does not include line ending + Buflen = Lrecl + Ending + ((Bin) ? 1 : 0); + + if (trace) + htrc("SubAllocating a buffer of %d bytes\n", Buflen); + + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (UseTemp || mode == MODE_DELETE) { + // Have a big buffer to move lines + Dbflen = Buflen * DOS_BUFF_LEN; + DelBuf = PlugSubAlloc(g, NULL, Dbflen); + } else if (mode == MODE_INSERT) { + /*******************************************************************/ + /* Prepare the buffer so eventual gaps are filled with blanks. */ + /*******************************************************************/ + memset(To_Buf, ' ', Buflen); + To_Buf[Buflen - 2] = '\n'; + To_Buf[Buflen - 1] = '\0'; + } // endif's mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int DOSFAM::GetRowID(void) + { + return Rows; + } // end of GetRowID + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int DOSFAM::GetPos(void) + { + return Fpos; + } // end of GetPos + +/***********************************************************************/ +/* GetNextPos: return the position of next record. */ +/***********************************************************************/ +int DOSFAM::GetNextPos(void) + { + return ftell(Stream); + } // end of GetNextPos + +/***********************************************************************/ +/* SetPos: Replace the table at the specified position. */ +/***********************************************************************/ +bool DOSFAM::SetPos(PGLOBAL g, int pos) + { + Fpos = pos; + + if (fseek(Stream, Fpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos); + return true; + } // endif + + Placed = true; + return false; + } // end of SetPos + +/***********************************************************************/ +/* Record file position in case of UPDATE or DELETE. */ +/***********************************************************************/ +bool DOSFAM::RecordPos(PGLOBAL g) + { + if ((Fpos = ftell(Stream)) < 0) { + sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno)); + return true; + } // endif Fpos + + return false; + } // end of RecordPos + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int DOSFAM::SkipRecord(PGLOBAL g, bool header) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + // Skip this record + if (!fgets(To_Buf, Buflen, Stream)) { + if (feof(Stream)) + return RC_EF; + +#if defined(UNIX) || defined(UNIV_LINUX) + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0)); +#else + sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL)); +#endif + return RC_FX; + } // endif fgets + + // Update progress information + dup->ProgCur = GetPos(); + + if (header) { + // For Delete + Fpos = ftell(Stream); + + if (!UseTemp) + Tpos = Spos = Fpos; // No need to move header + + } // endif header + +#if defined(THREAD) + return RC_NF; // To have progress info +#else + return RC_OK; // To loop locally +#endif + } // end of SkipRecord + +/***********************************************************************/ +/* ReadBuffer: Read one line for a text file. */ +/***********************************************************************/ +int DOSFAM::ReadBuffer(PGLOBAL g) + { + char *p; + int rc; + + if (!Stream) + return RC_EF; + + if (trace) + htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n", + Tdbp, Tdbp->To_Line, Placed); + + if (!Placed) { + /*******************************************************************/ + /* Record file position in case of UPDATE or DELETE. */ + /*******************************************************************/ + if (RecordPos(g)) + return RC_FX; + + CurBlk = (int)Rows++; + + if (trace) + htrc("ReadBuffer: CurBlk=%d\n", CurBlk); + + } else + Placed = false; + + if (trace) + htrc(" About to read: stream=%p To_Buf=%p Buflen=%d\n", + Stream, To_Buf, Buflen); + + if (fgets(To_Buf, Buflen, Stream)) { + p = To_Buf + strlen(To_Buf) - 1; + + if (trace) + htrc(" Read: To_Buf=%p p=%c\n", To_Buf, To_Buf, p); + +#if defined(UNIX) + if (true) { + // Data files can be imported from Windows (having CRLF) +#else + if (Bin) { + // Data file is read in binary so CRLF remains +#endif + if (*p == '\n' || *p == '\r') { + // is this enough for Unix ??? + *p = '\0'; // Eliminate ending CR or LF character + + if (p > To_Buf) { + // is this enough for Unix ??? + p--; + + if (*p == '\n' || *p == '\r') + *p = '\0'; // Eliminate ending CR or LF character + + } // endif To_Buf + + } // endif p + + } else if (*p == '\n') + *p = '\0'; // Eliminate ending new-line character + + if (trace) + htrc(" To_Buf='%s'\n", To_Buf); + + strcpy(Tdbp->To_Line, To_Buf); + num_read++; + rc = RC_OK; + } else if (feof(Stream)) { + rc = RC_EF; + } else { +#if defined(UNIX) + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0)); +#else + sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL)); +#endif + + if (trace) + htrc("%s\n", g->Message); + + rc = RC_FX; + } // endif's fgets + + if (trace) + htrc("ReadBuffer: rc=%d\n", rc); + + IsRead = true; + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for DOS access method. */ +/***********************************************************************/ +int DOSFAM::WriteBuffer(PGLOBAL g) + { + char *crlf = "\n"; + int curpos = 0; + bool moved = true; + + // T_Stream is the temporary stream or the table file stream itself + if (!T_Stream) + if (UseTemp && Tdbp->Mode == MODE_UPDATE) { + if (OpenTempFile(g)) + return RC_FX; + + } else + T_Stream = Stream; + + if (Tdbp->Mode == MODE_UPDATE) { + /*******************************************************************/ + /* Here we simply rewrite a record on itself. There are two cases */ + /* were another method should be used, a/ when Update apply to */ + /* the whole file, b/ when updating the last field of a variable */ + /* length file. The method could be to rewrite a new file, then */ + /* to erase the old one and rename the new updated file. */ + /*******************************************************************/ + curpos = ftell(Stream); + + if (trace) + htrc("Last : %d cur: %d\n", Fpos, curpos); + + if (UseTemp) { + /*****************************************************************/ + /* We are using a temporary file. Before writing the updated */ + /* record, we must eventually copy all the intermediate records */ + /* that have not been updated. */ + /*****************************************************************/ + if (MoveIntermediateLines(g, &moved)) + return RC_FX; + + Spos = curpos; // New start position + } else + // Update is directly written back into the file, + // with this (fast) method, record size cannot change. + if (fseek(Stream, Fpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), 0); + return RC_FX; + } // endif + + } // endif mode + + /*********************************************************************/ + /* Prepare the write buffer. */ + /*********************************************************************/ +#if defined(WIN32) + if (Bin) + crlf = "\r\n"; +#endif // WIN32 + strcat(strcpy(To_Buf, Tdbp->To_Line), crlf); + + /*********************************************************************/ + /* Now start the writing process. */ + /*********************************************************************/ + if ((fputs(To_Buf, T_Stream)) == EOF) { + sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno)); + return RC_FX; + } // endif EOF + + if (Tdbp->Mode == MODE_UPDATE && moved) + if (fseek(Stream, curpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return RC_FX; + } // endif + + if (trace) + htrc("write done\n"); + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for DOS and BLK access methods. */ +/***********************************************************************/ +int DOSFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool moved; + int curpos = ftell(Stream); + + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This will be experimented, but method 1 must be used for Unix as */ + /* the function needed to erase trailing records is not available. */ + /*********************************************************************/ + if (trace) + htrc( + "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, curpos, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + fseek(Stream, 0, SEEK_END); + Fpos = ftell(Stream); + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + } // endif irc + + if (Tpos == Spos) { + /*******************************************************************/ + /* First line to delete, Open temporary file. */ + /*******************************************************************/ + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + T_Stream = Stream; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &moved)) + return RC_FX; + + if (irc == RC_OK) { + /*******************************************************************/ + /* Reposition the file pointer and set Spos. */ + /*******************************************************************/ + if (!UseTemp || moved) + if (fseek(Stream, curpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), 0); + return RC_FX; + } // endif + + Spos = GetNextPos(); // New start position + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* The UseTemp case is treated in CloseTableFile. */ + /*******************************************************************/ + if (!UseTemp) { + /*****************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS ??) this is still to be checked */ + /* for compatibility with Text files and other OS's. */ + /*****************************************************************/ + char filename[_MAX_PATH]; + int h, rc; // File handle, return code + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + rc = PlugCloseFile(g, To_Fb); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)Tpos)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Tpos)) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } // endif !UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool DOSFAM::OpenTempFile(PGLOBAL g) + { + char tempname[_MAX_PATH]; + bool rc = false; + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) { + if (trace) + htrc("%s\n", g->Message); + + rc = true; + } else + To_Fbt = PlgGetUser(g)->Openlist; + + return rc; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/* This works only for file open in binary mode. */ +/***********************************************************************/ +bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int n; + size_t req, len; + + for (*b = false, n = Fpos - Spos; n > 0; n -= req) { + if (!UseTemp || !*b) + if (fseek(Stream, Spos, SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + req = (size_t)min(n, Dbflen); + len = fread(DelBuf, 1, req, Stream); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp) + if (fseek(T_Stream, Tpos, SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (trace) + htrc("after write pos=%d\n", ftell(Stream)); + + Tpos += (int)req; + Spos += (int)req; + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + *b = true; + } // endfor n + + return false; + } // end of MoveIntermediate Lines + +/***********************************************************************/ +/* Delete the old file and rename the new temp file. */ +/***********************************************************************/ +int DOSFAM::RenameTempFile(PGLOBAL g) + { + char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH]; + int rc; + + if (!To_Fbt) + return RC_INFO; // Nothing to do ??? + + // This loop is necessary because, in case of join, + // To_File can have been open several times. + for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next) + rc = PlugCloseFile(g, fb); + + tempname = (char*)To_Fbt->Fname; + PlugSetPath(filename, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(filetemp, filename), ".ttt"); + remove(filetemp); // May still be there from previous error + + if (rename(filename, filetemp)) { // Save file for security + sprintf(g->Message, MSG(RENAME_ERROR), + filename, filetemp, strerror(errno)); + rc = RC_FX; + } else if (rename(tempname, filename)) { + sprintf(g->Message, MSG(RENAME_ERROR), + tempname, filename, strerror(errno)); + rc = rename(filetemp, filename); // Restore saved file + rc = RC_FX; + } else if (remove(filetemp)) { + sprintf(g->Message, MSG(REMOVE_ERROR), + filetemp, strerror(errno)); + rc = RC_INFO; // Acceptable + } else + rc = RC_OK; + + return rc; + } // end of RenameTempFile + +/***********************************************************************/ +/* Table file close routine for DOS access method. */ +/***********************************************************************/ +void DOSFAM::CloseTableFile(PGLOBAL g) + { + int rc; + + if (UseTemp && T_Stream) { + if (Tdbp->Mode == MODE_UPDATE) { + // Copy eventually remaining lines + bool b; + + fseek(Stream, 0, SEEK_END); + Fpos = ftell(Stream); + rc = MoveIntermediateLines(g, &b); + } // endif Mode + + // Delete the old file and rename the new temp file. + RenameTempFile(g); // Also close all files + } else { + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("DOS Close: closing %s rc=%d\n", To_File, rc); + + } // endif UseTemp + + Stream = NULL; // So we can know whether table is open + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for DOS access method. */ +/***********************************************************************/ +void DOSFAM::Rewind(void) + { + rewind(Stream); + Rows = 0; + OldBlk = CurBlk = -1; + } // end of Rewind + +/* --------------------------- Class BLKFAM -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp) + { + Blocked = true; + Block = tdp->GetBlock(); + Last = tdp->GetLast(); + Nrec = tdp->GetElemt(); + Closing = false; + BlkPos = tdp->GetTo_Pos(); + CurLine = NULL; + NxtLine = NULL; + OutBuf = NULL; + } // end of BLKFAM standard constructor + +BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp) + { + Closing = txfp->Closing; + CurLine = txfp->CurLine; + NxtLine = txfp->NxtLine; + OutBuf = txfp->OutBuf; + } // end of BLKFAM copy constructor + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void BLKFAM::Reset(void) + { + DOSFAM::Reset(); + Closing = false; + } // end of Reset + +/***********************************************************************/ +/* Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int BLKFAM::Cardinality(PGLOBAL g) + { + // Should not be called in this version + return (g) ? -1 : 0; +//return (g) ? (int)((Block - 1) * Nrec + Last) : 1; + } // end of Cardinality + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/***********************************************************************/ +int BLKFAM::MaxBlkSize(PGLOBAL g, int s) + { + int savcur = CurBlk; + int size; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + size += (CurBlk == Block - 1) ? Last : Nrec; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/***********************************************************************/ +/* Allocate the line buffer. For mode Delete or when a temp file is */ +/* used another big buffer has to be allocated because is it used */ +/* to move or update the lines into the (temp) file. */ +/***********************************************************************/ +bool BLKFAM::AllocateBuffer(PGLOBAL g) + { + int len; + MODE mode = Tdbp->GetMode(); + + // For variable length files, Lrecl does not include CRLF + len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending); + Buflen = len * Nrec; + CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (UseTemp || mode == MODE_DELETE) { + if (mode == MODE_UPDATE) + OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1); + + Dbflen = Buflen; + DelBuf = PlugSubAlloc(g, NULL, Dbflen); + } else if (mode == MODE_INSERT) + Rbuf = Nrec; // To be used by WriteDB + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int BLKFAM::GetRowID(void) + { + return CurNum + Nrec * CurBlk + 1; + } // end of GetRowID + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int BLKFAM::GetPos(void) + { + return (CurNum + Nrec * CurBlk); // Computed file index + } // end of GetPos + +/***********************************************************************/ +/* GetNextPos: called by DeleteRecords. */ +/***********************************************************************/ +int BLKFAM::GetNextPos(void) + { + return Fpos + NxtLine - CurLine; + } // end of GetNextPos + +/***********************************************************************/ +/* SetPos: Replace the table at the specified position. */ +/***********************************************************************/ +bool BLKFAM::SetPos(PGLOBAL g, int pos) + { + if (pos < 0) { + strcpy(g->Message, MSG(INV_REC_POS)); + return true; + } // endif recpos + + CurBlk = pos / Nrec; + CurNum = pos % Nrec; +#if defined(_DEBUG) + num_eq[(CurBlk == OldBlk) ? 1 : 0]++; +#endif + + // Indicate the table position was externally set + Placed = true; + return false; + } // end of SetPos + +/***********************************************************************/ +/* Record file position in case of UPDATE or DELETE. */ +/* Not used yet for blocked tables. */ +/***********************************************************************/ +bool BLKFAM::RecordPos(PGLOBAL g) + { + Fpos = (CurNum + Nrec * CurBlk); // Computed file index + return false; + } // end of RecordPos + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int BLKFAM::SkipRecord(PGLOBAL g, bool header) + { + if (header) { + // For Delete + Fpos = BlkPos[0]; // First block starts after the header + + if (!UseTemp) + Tpos = Spos = Fpos; // No need to move header + + } // endif header + + OldBlk = -2; // To force fseek on first block + return RC_OK; + } // end of SkipRecord + +/***********************************************************************/ +/* ReadBuffer: Read one line for a text file. */ +/***********************************************************************/ +int BLKFAM::ReadBuffer(PGLOBAL g) + { + int i, n, rc = RC_OK; + + /*********************************************************************/ + /* Sequential reading when Placed is not true. */ + /*********************************************************************/ + if (Placed) { + Placed = false; + } else if (++CurNum < Rbuf) { + CurLine = NxtLine; + + // Get the position of the next line in the buffer + while (*NxtLine++ != '\n') ; + + // Set caller line buffer + n = NxtLine - CurLine - Ending; + memcpy(Tdbp->GetLine(), CurLine, n); + Tdbp->GetLine()[n] = '\0'; + goto fin; + } else if (Rbuf < Nrec && CurBlk != -1) { + return RC_EF; + } else { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + if (++CurBlk >= Block) + return RC_EF; + + } // endif's + + if (OldBlk == CurBlk) + goto ok; // Block is already there + + // fseek is required only in non sequential reading + if (CurBlk != OldBlk + 1) + if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]); + return RC_FX; + } // endif fseek + + // Calculate the length of block to read + BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk]; + + if (trace) + htrc("File position is now %d\n", ftell(Stream)); + + // Read the entire next block + n = fread(To_Buf, 1, (size_t)BlkLen, Stream); + + if (n == BlkLen) { +// ReadBlks++; + num_read++; + Rbuf = (CurBlk == Block - 1) ? Last : Nrec; + + ok: + rc = RC_OK; + + // Get the position of the current line + for (i = 0, CurLine = To_Buf; i < CurNum; i++) + while (*CurLine++ != '\n') ; // What about Unix ??? + + // Now get the position of the next line + for (NxtLine = CurLine; *NxtLine++ != '\n';) ; + + // Set caller line buffer + n = NxtLine - CurLine - Ending; + memcpy(Tdbp->GetLine(), CurLine, n); + Tdbp->GetLine()[n] = '\0'; + } else if (feof(Stream)) { + rc = RC_EF; + } else { +#if defined(UNIX) + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno)); +#else + sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL)); +#endif + + if (trace) + htrc("%s\n", g->Message); + + return RC_FX; + } // endelse + + OldBlk = CurBlk; // Last block actually read + IsRead = true; // Is read indeed + + fin: + // Store the current record file position for Delete and Update + Fpos = BlkPos[CurBlk] + CurLine - To_Buf; + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: File write routine for the blocked DOS access method. */ +/* Update is directly written back into the file, */ +/* with this (fast) method, record size cannot change. */ +/***********************************************************************/ +int BLKFAM::WriteBuffer(PGLOBAL g) + { + if (Tdbp->GetMode() == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode, blocks are added sequentially to the file end. */ + /*******************************************************************/ + if (!Closing) { // Add line to the write buffer + strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf); + + if (++CurNum != Rbuf) { + CurLine += strlen(CurLine); + return RC_OK; // We write only full blocks + } // endif CurNum + + } // endif Closing + + // Now start the writing process. + NxtLine = CurLine + strlen(CurLine); + BlkLen = NxtLine - To_Buf; + + if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) { + sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno)); + Closing = true; // To tell CloseDB about a Write error + return RC_FX; + } // endif size + + CurBlk++; + CurNum = 0; + CurLine = To_Buf; + } else { + /*******************************************************************/ + /* Mode == MODE_UPDATE. */ + /*******************************************************************/ + char *crlf; + size_t len; + int curpos = ftell(Stream); + bool moved = true; + + // T_Stream is the temporary stream or the table file stream itself + if (!T_Stream) + if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) { + if (OpenTempFile(g)) + return RC_FX; + + } else + T_Stream = Stream; + + if (UseTemp) { + /*****************************************************************/ + /* We are using a temporary file. Before writing the updated */ + /* record, we must eventually copy all the intermediate records */ + /* that have not been updated. */ + /*****************************************************************/ + if (MoveIntermediateLines(g, &moved)) + return RC_FX; + + Spos = GetNextPos(); // New start position + + // Prepare the output buffer +#if defined(WIN32) + crlf = "\r\n"; +#else + crlf = "\n"; +#endif // WIN32 + strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf); + len = strlen(OutBuf); + } else { + if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position + sprintf(g->Message, MSG(FSETPOS_ERROR), 0); + return RC_FX; + } // endif fseek + + // Replace the line inside read buffer (length has not changed) + memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine())); + OutBuf = CurLine; + len = (size_t)(NxtLine - CurLine); + } // endif UseTemp + + if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) { + sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno)); + return RC_FX; + } // endif fwrite + + if (moved) + if (fseek(Stream, curpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return RC_FX; + } // endif + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Table file close routine for DOS access method. */ +/***********************************************************************/ +void BLKFAM::CloseTableFile(PGLOBAL g) + { + int rc, wrc = RC_OK; + + if (UseTemp && T_Stream) { + if (Tdbp->GetMode() == MODE_UPDATE) { + // Copy eventually remaining lines + bool b; + + fseek(Stream, 0, SEEK_END); + Fpos = ftell(Stream); + rc = MoveIntermediateLines(g, &b); + } else + rc = RC_OK; + + if (rc == RC_OK) + // Delete the old file and rename the new temp file. + rc = RenameTempFile(g); // Also close all files + else + rc = PlugCloseFile(g, To_Fb); + + } else { + // Closing is True if last Write was in error + if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) { + // Some more inserted lines remain to be written + Rbuf = CurNum--; + Closing = true; + wrc = WriteBuffer(g); + } else if (Modif && !Closing) { + // Last updated block remains to be written + Closing = true; + wrc = ReadBuffer(g); + } // endif's + + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n", + To_File, Tdbp->GetMode(), wrc, rc); + + } // endif UseTemp + + Stream = NULL; // So we can know whether table is open + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for DOS access method. */ +/* Note: commenting out OldBlk = -1 has two advantages: */ +/* 1 - It forces fseek on first block, thus suppressing the need to */ +/* rewind the file, anyway unuseful when second pass if indexed. */ +/* 2 - It permit to avoid re-reading small tables having only 1 block.*/ +/***********************************************************************/ +void BLKFAM::Rewind(void) + { +//rewind(Stream); will be placed by fseek + CurBlk = -1; + CurNum = Rbuf; +//OldBlk = -1; commented out in case we reuse last read block +//Rbuf = 0; commented out in case we reuse last read block + } // end of Rewind + diff --git a/storage/connect/filamtxt.h b/storage/connect/filamtxt.h new file mode 100644 index 00000000000..6a1093eaa06 --- /dev/null +++ b/storage/connect/filamtxt.h @@ -0,0 +1,198 @@ +/************** FilAMTxt H Declares Source Code File (.H) **************/ +/* Name: FILAMTXT.H Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the file access method classes declares. */ +/***********************************************************************/ + +#ifndef __FILAMTXT_H +#define __FILAMTXT_H + +#include "block.h" + +typedef class TXTFAM *PTXF; +typedef class DOSFAM *PDOSFAM; +typedef class BLKFAM *PBLKFAM; +typedef class DOSDEF *PDOSDEF; +typedef class TDBDOS *PTDBDOS; + +#define TYPE_AM_BLK (AMT)160 + +/***********************************************************************/ +/* This is the base class for all file access method classes. */ +/***********************************************************************/ +class DllExport TXTFAM : public BLOCK { + friend class TDBDOS; + friend class TDBCSV; + friend class TDBFIX; + friend class TDBVCT; + friend class DOSCOL; + friend class BINCOL; + friend class VCTCOL; + public: + // Constructor + TXTFAM(PDOSDEF tdp); + TXTFAM(PTXF txfp); + + // Implementation + virtual AMT GetAmType(void) = 0; + virtual int GetPos(void) = 0; + virtual int GetNextPos(void) = 0; + virtual PTXF Duplicate(PGLOBAL g) = 0; + virtual bool GetUseTemp(void) {return false;} + virtual int GetDelRows(void) {return DelRows;} + int GetCurBlk(void) {return CurBlk;} + void SetTdbp(PTDBDOS tdbp) {Tdbp = tdbp;} + int GetBlock(void) {return Block;} + void SetBlkPos(int *bkp) {BlkPos = bkp;} + void SetNrec(int n) {Nrec = n;} + char *GetBuf(void) {return To_Buf;} + int GetRows(void) {return Rows;} + bool IsBlocked(void) {return Blocked;} + + // Methods + virtual void Reset(void); + virtual int GetFileLength(PGLOBAL g); + virtual int Cardinality(PGLOBAL g); + virtual int MaxBlkSize(PGLOBAL g, int s); + virtual bool AllocateBuffer(PGLOBAL g) {return false;} + virtual void ResetBuffer(PGLOBAL g) {} + virtual int GetNerr(void) {return 0;} + virtual int GetRowID(void) = 0; + virtual bool RecordPos(PGLOBAL g) = 0; + virtual bool SetPos(PGLOBAL g, int recpos) = 0; + virtual int SkipRecord(PGLOBAL g, bool header) = 0; + virtual bool OpenTableFile(PGLOBAL g) = 0; + virtual bool DeferReading(void) {IsRead = false; return true;} + virtual int ReadBuffer(PGLOBAL g) = 0; + virtual int WriteBuffer(PGLOBAL g) = 0; + virtual int DeleteRecords(PGLOBAL g, int irc) = 0; + virtual void CloseTableFile(PGLOBAL g) = 0; + virtual void Rewind(void) = 0; + + protected: + // Members + PTDBDOS Tdbp; // To table class + PSZ To_File; // Points to table file name + PFBLOCK To_Fb; // Pointer to file block + bool Placed; // true if Recpos was externally set + bool IsRead; // false for deferred reading + bool Blocked; // true if using blocked I/O + char *To_Buf; // Points to I/O buffer + void *DelBuf; // Buffer used to move lines in Delete + int *BlkPos; // To array of block positions + int BlkLen; // Current block length + int Buflen; // Buffer length + int Dbflen; // Delete buffer length + int Rows; // Number of rows read so far + int DelRows; // Number of deleted rows + int Headlen; // Number of bytes in header + int Lrecl; // Logical Record Length + int Block; // Number of blocks in table + int Last; // Number of elements of last block + int Nrec; // Number of records in buffer + int OldBlk; // Index of last read block + int CurBlk; // Index of current block + int CurNum; // Current buffer line number + int ReadBlks; // Number of blocks read (selected) + int Rbuf; // Number of lines read in buffer + int Modif; // Number of modified lines in block + int Blksize; // Size of padded blocks + int Ending; // Length of line end + bool Padded; // true if fixed size blocks are padded + bool Eof; // true if an EOF (0xA) character exists + char *CrLf; // End of line character(s) + }; // end of class TXTFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for standard */ +/* text files with variable record format (DOS, CSV, FMT) */ +/***********************************************************************/ +class DllExport DOSFAM : public TXTFAM { + public: + // Constructor + DOSFAM(PDOSDEF tdp); + DOSFAM(PDOSFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_DOS;} + virtual bool GetUseTemp(void) {return UseTemp;} + virtual int GetPos(void); + virtual int GetNextPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) DOSFAM(this);} + + // Methods + virtual void Reset(void); + virtual int GetFileLength(PGLOBAL g); + virtual int Cardinality(PGLOBAL g); + virtual int MaxBlkSize(PGLOBAL g, int s); + virtual bool AllocateBuffer(PGLOBAL g); + virtual int GetRowID(void); + virtual bool RecordPos(PGLOBAL g); + virtual bool SetPos(PGLOBAL g, int recpos); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual bool OpenTableFile(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + virtual bool OpenTempFile(PGLOBAL g); + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b); + virtual int RenameTempFile(PGLOBAL g); + + // Members + FILE *Stream; // Points to Dos file structure + FILE *T_Stream; // Points to temporary file structure + PFBLOCK To_Fbt; // Pointer to temp file block + int Fpos; // Position of last read record + int Tpos; // Target Position for delete move + int Spos; // Start position for delete move + bool UseTemp; // True to use a temporary file in Delete + bool Bin; // True to force binary mode + }; // end of class DOSFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for standard */ +/* text files with variable record format (DOS, CSV, FMT) */ +/***********************************************************************/ +class DllExport BLKFAM : public DOSFAM { + public: + // Constructor + BLKFAM(PDOSDEF tdp); + BLKFAM(PBLKFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_BLK;} + virtual int GetPos(void); + virtual int GetNextPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) BLKFAM(this);} + + // Methods + virtual void Reset(void); + virtual int Cardinality(PGLOBAL g); + virtual int MaxBlkSize(PGLOBAL g, int s); + virtual bool AllocateBuffer(PGLOBAL g); + virtual int GetRowID(void); + virtual bool RecordPos(PGLOBAL g); + virtual bool SetPos(PGLOBAL g, int recpos); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + // Members + char *CurLine; // Position of current line in buffer + char *NxtLine; // Position of Next line in buffer + char *OutBuf; // Buffer to write in temporary file + bool Closing; // True when closing on Update + }; // end of class BLKFAM + +#endif // __FILAMTXT_H diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp new file mode 100755 index 00000000000..47ac07c6554 --- /dev/null +++ b/storage/connect/filamvct.cpp @@ -0,0 +1,4222 @@ +/*********** File AM Vct C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMVCT */ +/* ------------- */ +/* Version 2.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the VCT file access method classes. */ +/* Added in version 2: F */ +/* - Split Vec format. */ +/* - Partial delete. */ +/* - Use of tempfile for update. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLAND__ +//#include <windows.h> +#include <sys/stat.h> +#else // !WIN32 F +#if defined(UNIX) +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +//#define strerror(X) _strerror(X) +#define NO_ERROR 0 +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "osutil.h" // Unuseful for WIN32 +#include "plgdbsem.h" +#include "valblk.h" +#include "filamfix.h" +#include "tabdos.h" +#include "tabvct.h" +#include "maputil.h" +#include "filamvct.h" + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +extern int num_read, num_there; // Statistics +static int num_write; +extern "C" int trace; + +#if defined(UNIX) +// Add dummy strerror (NGC) +char *strerror(int num); +#endif // UNIX + +/***********************************************************************/ +/* Header containing block info for not split VEC tables. */ +/* Block and last values can be calculated from NumRec and Nrec. */ +/* This is better than directly storing Block and Last because it */ +/* make possible to use the same file with tables having a different */ +/* block size value (Element -> Nrec) */ +/* Note: can be in a separate file if header=1 or a true header (2) */ +/***********************************************************************/ +typedef struct _vecheader { +//int Block; /* The number of used blocks */ +//int Last; /* The number of used records in last block */ + int MaxRec; /* Max number of records (True vector format)*/ + int NumRec; /* Number of valid records in the table */ + } VECHEADER; + +/***********************************************************************/ +/* Char VCT column blocks are right filled with blanks (blank = true) */ +/* Conversion of block values allowed conditionally for insert only. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, + bool check = true, bool blank = true); + +/* -------------------------- Class VCTFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCTFAM class. */ +/***********************************************************************/ +VCTFAM::VCTFAM(PVCTDEF tdp) : FIXFAM((PDOSDEF)tdp) + { + Last = tdp->GetLast(); + MaxBlk = (tdp->GetEstimate() > 0) ? + ((tdp->GetEstimate() - 1) / Nrec + 1) : 0; + NewBlock = NULL; + AddBlock = false; + Split = false; + + if ((Header = (MaxBlk) ? tdp->Header : 0)) + Block = Last = -1; + + Bsize = Nrec; + CurNum = Nrec - 1; + Colfn = NULL; + Tempat = NULL; + Clens = NULL; + Deplac = NULL; + Isnum = NULL; + Ncol = 0; + } // end of VCTFAM standard constructor + +VCTFAM::VCTFAM(PVCTFAM txfp) : FIXFAM(txfp) + { + MaxBlk = txfp->MaxBlk; + NewBlock = NULL; + AddBlock = false; + Split = txfp->Split; + Header = txfp->Header; + Bsize = txfp->Bsize; + Colfn = txfp->Colfn; + Tempat = txfp->Tempat; + Clens = txfp->Clens; + Deplac = txfp->Deplac; + Isnum = txfp->Isnum; + Ncol = txfp->Ncol; + } // end of VCTFAM copy constructor + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void VCTFAM::Reset(void) + { + FIXFAM::Reset(); + NewBlock = NULL; + AddBlock = false; + CurNum = Nrec - 1; + } // end of Reset + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int VCTFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int h, k, n; + VECHEADER vh; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + + if ((h = global_open(g, MSGID_CANNOT_OPEN, filename, O_RDONLY)) == -1 + || !_filelength(h)) { + // Consider this is a void table + Last = Nrec; + Block = 0; + + if (h != -1) + close(h); + + return n; + } else if (Header == 3) + k = lseek(h, -(int)sizeof(VECHEADER), SEEK_END); + + if ((k = read(h, &vh, sizeof(vh))) != sizeof(vh)) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + } // endif s + + close(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +bool VCTFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc = false; + int k; + size_t n; + VECHEADER vh; + FILE *s; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Stream) { + s = Stream; + + if (Header == 1) + k = fseek(s, 0, SEEK_SET); + + } else + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r+b"); + + } else { // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + s= global_fopen(g, MSGID_CANNOT_OPEN, filename, "wb"); + } // endif Header + + if (!s) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } else if (Header == 3) + k = fseek(s, -(int)sizeof(VECHEADER), SEEK_END); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if ((n = fwrite(&vh, sizeof(vh), 1, s)) != 1) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || !Stream) + fclose(s); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/***********************************************************************/ +int VCTFAM::MaxBlkSize(PGLOBAL g, int s) + { + int savcur = CurBlk; + int size; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + size += (CurBlk == Block - 1) ? Last : Nrec; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/***********************************************************************/ +/* VCT Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int VCTFAM::Cardinality(PGLOBAL g) + { + if (!g) + return 1; + + if (Block < 0) + if (Split) { + // Separate column files and no pre setting of Block and Last + // This allows to see a table modified externally, but Block + // and Last must be set from the file cardinality. + // Only happens when called by sub classes. + char filename[_MAX_PATH]; + PSZ savfn = To_File; + int len, clen, card = -1; + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + // Use the first column file to calculate the cardinality + clen = cdp->GetClen(); + sprintf(filename, Colfn, 1); + To_File = filename; + len = GetFileLength(g); + To_File = savfn; + + if (len >= 0) { + if (!(len % clen)) + card = len / clen; // Fixed length file + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, clen); + + if (trace) + htrc(" Computed max_K=%d Filen=%d Clen=%d\n", card, len, clen); + + } else + card = 0; + + // Set number of blocks for later use + Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; + Last = (card + Nrec - 1) % Nrec + 1; + return card; + } else { + // Vector table having Block and Last info in a Header (file) + if ((Headlen = GetBlockInfo(g)) < 0) + return -1; // Error + + } // endif split + + return (int)((Block - 1) * Nrec + Last); + } // end of Cardinality + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int VCTFAM::GetRowID(void) + { + return 1 + ((CurBlk < Block) ? CurNum + Nrec * CurBlk + : (Block - 1) * Nrec + Last); + } // end of GetRowID + +/***********************************************************************/ +/* VCT Create an empty file for Vector formatted tables. */ +/***********************************************************************/ +bool VCTFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file: this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int h, n; + + PlugSetPath(filename, fn, Tdbp->GetPath()); +#if defined(WIN32) + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, _O_CREAT | _O_WRONLY, S_IREAD | S_IWRITE); +#else // !WIN32 + h= global_open(g, MSGID_OPEN_EMPTY_FILE, filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); +#endif // !WIN32 + + if (h == -1) + return true; + + n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + if (lseek(h, n + MaxBlk * Nrec * Lrecl - 1, SEEK_SET) == -1) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; + } // end of MakeEmptyFile + +/***********************************************************************/ +/* VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VCTFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "rb" : "r+b"); + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + strcpy(opmode, "r+b"); // Required to update empty blocks + } else if (Last == Nrec) + strcpy(opmode, "ab"); + else + strcpy(opmode, "r+b"); // Required to update the last block + + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Use conventionnal input/output functions. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Stream + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fb = dbuserp->Openlist; // Keep track of File block + + if (!strcmp(opmode, "wb")) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + num_read = num_there = num_write = 0; + + // Allocate the table and column block buffer + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VCTFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); // Initialize inserting + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0, n = (MaxBlk) ? MaxBlk : 1; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Deplac[i] = Headlen + cdp->GetPoff() * n * Nrec; + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCTFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Last block must be updated by new values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + } // endif Last + + // We are not currently using a temporary file for Insert + T_Stream = Stream; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* ReadBuffer: Read one line for a VCT file. */ +/***********************************************************************/ +int VCTFAM::ReadBuffer(PGLOBAL g) + { + int rc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (Placed) + Placed = false; + else if ((++CurNum) >= ((CurBlk < Block - 1) ? Nrec : Last)) { + /*******************************************************************/ + /* New block. */ + /*******************************************************************/ + CurNum = 0; + + if (++CurBlk == Block) + return RC_EF; // End of file + + num_there++; + } // endif CurNum + + if (OldBlk != CurBlk) { + if (mode == MODE_UPDATE) { + /*****************************************************************/ + /* Flush the eventually modified column buffers in old blocks */ + /* and read the blocks to modify attached to Set columns. */ + /*****************************************************************/ + if (MoveLines(g)) // For VECFAM + return RC_FX; + + for (PVCTCOL colp = (PVCTCOL)Tdbp->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) { + colp->WriteBlock(g); + colp->ReadBlock(g); + } // endfor colp + + } // endif mode + + OldBlk = CurBlk; // Last block actually read + } // endif oldblk + + if (trace) + htrc(" Read: CurNum=%d CurBlk=%d rc=%d\n", CurNum, CurBlk, RC_OK); + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VCTFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (!T_Stream) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + T_Stream = Stream; + + } // endif T_Stream + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // For VCT tables, future blocks must be added + char filename[_MAX_PATH]; + + // Close the file and reopen it in mode Insert + fclose(Stream); + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!(Stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "ab"))) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Stream + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if ((size_t)Nrec != + fwrite(NewBlock, (size_t)Lrecl, (size_t)Nrec, Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VCT access method. */ +/* Note: lines are moved directly in the files (ooops...) */ +/* Using temp file depends on the Check setting, false by default. */ +/***********************************************************************/ +int VCTFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + if (trace) + htrc("VCT DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just the setting of future Spos and Tpos. */ + /*****************************************************************/ + T_Stream = Stream; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { + /*******************************************************************/ + /* Reposition the file pointer and set Spos. */ + /*******************************************************************/ +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* Update the Block and Last values. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + /***************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS ??) this is still to be */ + /* checked for compatibility with Text files and other OS's. */ + /***************************************************************/ + char filename[_MAX_PATH]; + int rc, h; + + rc = CleanUnusedSpace(g); // Clean last block + rc = PlugCloseFile(g, To_Fb); + Stream = NULL; // For SetBlockInfo + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra blocks. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Headlen + Block * Blksize))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Headlen + Block * Blksize)) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } else + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool VCTFAM::OpenTempFile(PGLOBAL g) + { + char *opmode, tempname[_MAX_PATH]; + bool rc = false; + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (MaxBlk) { + if (MakeEmptyFile(g, tempname)) + return true; + + opmode = "r+b"; + } else + opmode = "wb"; + + if (!(T_Stream = PlugOpenFile(g, tempname, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + rc = true; + } else + To_Fbt = PlgGetUser(g)->Openlist; + + return rc; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VCTFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, dep, off; + int n; + bool eof = (b) ? *b : false; + size_t req, len; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (size_t)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (size_t)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (MaxBlk) { + dep = Deplac[i]; + off = Spos * Clens[i]; + } else { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + dep = Deplac[i] + (Spos / Nrec) * Blksize; + off = (Spos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Stream); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp || MaxBlk) { + if (MaxBlk) { + dep = Deplac[i]; + off = Tpos * Clens[i]; + } else { + dep = Deplac[i] + (Tpos / Nrec) * Blksize; + off = (Tpos % Nrec) * Clens[i]; + } // endif MaxBlk + + if (fseek(T_Stream, dep + off, SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endif UseTemp + + if (trace) + htrc("after write pos=%d\n", ftell(Stream)); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (Tpos % Nrec == 0 || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, + // must be done here because T_Stream was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + // Write a new block in the temporary file + len = (size_t)Blksize; + + if (fwrite(NewBlock, 1, len, T_Stream) != len) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (Spos == Fpos) + eof = false; + + } // endif UseTemp + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a VCT or Vec table file. */ +/***********************************************************************/ +bool VCTFAM::CleanUnusedSpace(PGLOBAL g) + { + int i, dep; + int n; + size_t req, len; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); + + if (!(n = Nrec - Last)) + return false; + + dep = (Block - 1) * Blksize; + req = (size_t)n; + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + if (fseek(Stream, dep + Deplac[i] + Last * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + } else for (n = Fpos - Tpos; n > 0; n -= req) { + /*******************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* Note: this seems to work even column blocks have been made */ + /* with Blanks = true. Perhaps should it be set to false for VEC. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + memset(To_Buf, 0, Buflen); + + for (i = 0; i < Ncol; i++) { + if (fseek(T_Stream, Deplac[i] + Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Stream)) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + } // endfor i + + Tpos += (int)req; + } // endfor n + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && T_Stream) { + rc = RenameTempFile(g); + + if (Header) { + // Header must be set because it was not set in temp file + Stream = T_Stream = NULL; // For SetBlockInfo + rc = SetBlockInfo(g); + } // endif Header + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && T_Stream) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Stream = T_Stream = NULL; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (!(UseTemp && T_Stream)) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Stream = NULL; + } // end of CloseTableFile + +/***********************************************************************/ +/* Data Base close routine for VCT access method. */ +/***********************************************************************/ +bool VCTFAM::ResetTableSize(PGLOBAL g, int block, int last) + { + bool rc = false; + + // Set Block and Last values for TDBVCT::MakeBlockValues + Block = block; + Last = last; + + if (!Split) { + if (!Header) { + // Update catalog values for Block and Last + PVCTDEF defp = (PVCTDEF)Tdbp->GetDef(); + LPCSTR name = Tdbp->GetName(); + PCATLG cat = PlgGetCatalog(g); + + defp->SetBlock(Block); + defp->SetLast(Last); + + if (!cat->SetIntCatInfo("Blocks", Block) || + !cat->SetIntCatInfo("Last", Last)) { + sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); + rc = true; + } // endif + + } else + rc = SetBlockInfo(g); + + } // endif Split + + Tdbp->ResetSize(); + return rc; + } // end of ResetTableSize + +/***********************************************************************/ +/* Rewind routine for VCT access method. */ +/***********************************************************************/ +void VCTFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; +//rewind(Stream); will be placed by fseek + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCTFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // True vector format + len = Headlen + Nrec * (colp->Deplac * MaxBlk + colp->Clen * CurBlk); + else // Blocked vector format + len = Nrec * (colp->Deplac + Lrecl * CurBlk); + + if (trace) + htrc("len=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d maxblk=%d\n", + len, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (fseek(Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Stream); + + if (n != (size_t)Nrec) { + if (errno == NO_ERROR) + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, To_File); + else + sprintf(g->Message, MSG(READ_ERROR), + To_File, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VCTFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + len = Headlen + + Nrec * (colp->Deplac * MaxBlk + colp->Clen * colp->ColBlk); + else // Old VCT format + len = Nrec * (colp->Deplac + Lrecl * colp->ColBlk); + + if (trace) + htrc("modif=%d len=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (fseek(T_Stream, len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Stream)) { + sprintf(g->Message, MSG(WRITE_STRERROR), + (UseTemp) ? To_Fbt->Fname : To_File, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } // endif + +#if defined(UNIX) + fflush(T_Stream); //NGC +#endif + +#ifdef _DEBUG + num_write++; +#endif + + return false; + } // end of WriteBlock + +/* -------------------------- Class VCMFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VCMFAM class. */ +/***********************************************************************/ +VCMFAM::VCMFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Memory = NULL; + Memcol = NULL; + } // end of VCMFAM standard constructor + +VCMFAM::VCMFAM(PVCMFAM txfp) : VCTFAM(txfp) + { + Memory = txfp->Memory; + Memcol = txfp->Memcol; + } // end of VCMFAM copy constructor + +/***********************************************************************/ +/* Mapped VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VCMFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + int len; + MODE mode = Tdbp->GetMode(); + PFBLOCK fp = NULL; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + /*********************************************************************/ + /* We used the file name relative to recorded datapath. */ + /*********************************************************************/ + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as if it were */ + /* entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dbuserp->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping VCM file, fp=%p cnt=%d\n", fp, fp->Count); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memory = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* If required, delete the whole file if no filtering is implied. */ + /*******************************************************************/ + bool del; + HANDLE hFile; + MEMMAP mm; + MODE mapmode = mode; + + if (mode == MODE_INSERT) { + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Inserting will be like updating the file + mapmode = MODE_UPDATE; + } else { + strcpy(g->Message, "MAP Insert is for VEC Estimate tables only"); + return true; + } // endif MaxBlk + + } // endif mode + + del = mode == MODE_DELETE && !Tdbp->GetNext(); + + if (del) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + } // endif del + + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mapmode, del); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*******************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*******************************************************************/ + len = mm.lenL; + Memory = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + bool rc = ResetTableSize(g, 0, Nrec); + return (mapmode == MODE_UPDATE) ? true : rc; + } // endif len + + if (!Memory) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memory; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fb = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n", + fp, fp->Count, Memory, len); + + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/* Note: Memcol must be set for all columns because it can be used */ +/* for set columns in Update. Clens values are used only in Delete. */ +/***********************************************************************/ +bool VCMFAM::AllocateBuffer(PGLOBAL g) + { + int m, i = 0; + bool b = Tdbp->GetMode() == MODE_DELETE; + PVCTCOL cp; + PCOLDEF cdp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + // Calculate the number of columns + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + // To store the start position of each column + Memcol = (char**)PlugSubAlloc(g, NULL, Ncol * sizeof(char*)); + m = (MaxBlk) ? MaxBlk : 1; + + // We will need all column sizes and type for Delete + if (b) { + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + } // endif b + + for (cdp = defp->GetCols(); i < Ncol; i++, cdp = cdp->GetNext()) { + if (b) { + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + } // endif b + + Memcol[i] = Memory + Headlen + cdp->GetPoff() * m * Nrec; + } // endfor cdp + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + if (Tdbp->GetMode() == MODE_INSERT) + return InitInsert(g); + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VCMFAM::InitInsert(PGLOBAL g) + { + int rc; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // We come here in MODE_INSERT only + if (Last == Nrec) { + CurBlk = Block; + CurNum = 0; + AddBlock = !MaxBlk; + } else { + // The starting point must be at the end of file as for append. + CurBlk = Block - 1; + CurNum = Last; + } // endif Last + + // Prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return true; + } // endif + + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + g->jump_level--; + return true; + } // endif + + // Initialize the column block pointer + for (; cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + g->jump_level--; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Data Base write routine for VMP access method. */ +/***********************************************************************/ +int VCMFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCM WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + // Mode Update being done in ReadDB we process here Insert mode only. + if (Tdbp->GetMode() == MODE_INSERT) { + if (CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing) { + CurBlk++; + CurNum = 0; + + // Re-initialize the column block pointer + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + cp->ReadBlock(g); + + } // endif Closing + + } // endif Closing || CurNum + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VCMFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VCM DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if (Fpos > Spos) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) { + // Old VCT format, moving must respect block limits + char *ps, *pt; + int req, soff, toff; + + for (n = Fpos - Spos; n > 0; n -= req) { + soff = Spos % Nrec; + toff = Tpos % Nrec; + req = (size_t)min(n, Nrec - max(soff, toff)); + + for (i = 0; i < Ncol; i++) { + ps = Memcol[i] + (Spos / Nrec) * Blksize + soff * Clens[i]; + pt = Memcol[i] + (Tpos / Nrec) * Blksize + toff * Clens[i]; + memmove(pt, ps, req * Clens[i]); + } // endfor i + + Tpos += req; + Spos += req; + } // endfor n + + } else { + // True vector format, all is simple... + n = Fpos - Spos; + + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, n * m); + } // endfor i + + Tpos += n; + } // endif MaxBlk + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. Reset the Block and */ + /* Last values for TDBVCT::MakeBlockValues. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!MaxBlk) { + PFBLOCK fp = To_Fb; + + // Clean the unused part of the last block + m = (Block - 1) * Blksize; + n = Nrec - Last; + + for (i = 0; i < Ncol; i++) + memset(Memcol[i] + m + Last * Clens[i], + (Isnum[i]) ? 0 : ' ', n * Clens[i]); + + // We must Unmap the view and use the saved file handle + // to put an EOF at the end of the last block of the file. + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + // Remove extra blocks + n = Block * Blksize; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } else + // True vector table, Table file size does not change. + // Just clean the unused part of the file. + for (n = Fpos - Tpos, i = 0; i < Ncol; i++) + memset(Memcol[i] + Tpos * Clens[i], 0, n * Clens[i]); + + // Reset Last and Block values in the catalog + PlugCloseFile(g, To_Fb); // in case of Header + ResetTableSize(g, Block, Last); + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VCMFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (!Closing) { + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + } else + wrc = RC_FX; // Last write was in error + + PlugCloseFile(g, To_Fb); + + if (wrc != RC_FX) + rc = ResetTableSize(g, Block, Last); + + } else if (mode != MODE_DELETE) + PlugCloseFile(g, To_Fb); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VCMFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + char *mempos; + int i = colp->Index - 1; + int n = Nrec * ((MaxBlk || Split) ? colp->Clen : Lrecl); + + /*********************************************************************/ + /* Calculate the start position of the column block to read. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("mempos=%p i=%d Nrec=%d Clen=%d CurBlk=%d\n", + mempos, i, Nrec, colp->Clen, CurBlk); + + if (colp->GetStatus(BUF_MAPPED)) + colp->Blk->SetValPointer(mempos); + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: there is nothing to do because we are working directly into */ +/* the mapped file, except when checking for Update but in this case */ +/* we do not want to write back the modifications either. */ +/***********************************************************************/ +bool VCMFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { +#if defined(_DEBUG) + char *mempos; + int i = colp->Index - 1; + int n = Nrec * colp->Clen; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + mempos = Memcol[i] + n * CurBlk; + + if (trace) + htrc("modif=%d mempos=%p i=%d Nrec=%d Clen=%d colblk=%d\n", + Modif, mempos, i, Nrec, colp->Clen, colp->ColBlk); + +#endif // _DEBUG + + return false; + } // end of WriteBlock + +/* -------------------------- Class VECFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VECFAM class. */ +/***********************************************************************/ +VECFAM::VECFAM(PVCTDEF tdp) : VCTFAM((PVCTDEF)tdp) + { + Streams = NULL; + To_Fbs = NULL; + To_Bufs = NULL; + Split = true; + Block = Last = -1; + InitUpdate = false; + } // end of VECFAM standard constructor + +VECFAM::VECFAM(PVECFAM txfp) : VCTFAM(txfp) + { + Streams = txfp->Streams; + To_Fbs = txfp->To_Fbs; + Clens = txfp->Clens; + To_Bufs = txfp->To_Bufs; + InitUpdate = txfp->InitUpdate; + } // end of VECFAM copy constructor + +/***********************************************************************/ +/* VEC Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VECFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4]; + int i; + bool b; + PCOLDEF cdp; + PVCTCOL cp; + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + /*********************************************************************/ + /* Call Cardinality to set Block and Last values in case it was not */ + /* already called (this happens indeed in test xmode) */ + /*********************************************************************/ + Cardinality(g); + + /*********************************************************************/ + /* Open according to input/output mode required. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + strcpy(opmode, "rb"); + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will delete the whole file + strcpy(opmode, "wb"); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + break; + } // endif filter + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + strcpy(opmode, (UseTemp) ? "r": "r+"); + break; + case MODE_INSERT: + strcpy(opmode, "ab"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern and set Ncol + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_INSERT || mode == MODE_DELETE) { + // All columns must be written or deleted + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (OpenColumnFile(g, opmode, i)) + return true; + + // Check for void table or missing columns + for (b = !Streams[0], i = 1; i < Ncol; i++) + if (b != !Streams[i]) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding to updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (OpenColumnFile(g, opmode, cp->Index - 1)) + return true; + + // Open in read only mode the used columns not already open + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Streams[cp->Index - 1]) + if (OpenColumnFile(g, "rb", cp->Index - 1)) + return true; + + // Check for void table or missing columns + for (i = 0, cp = (PVCTCOL)Tdbp->GetColumns(); cp; + cp = (PVCTCOL)cp->Next) + if (!i++) + b = !Streams[cp->Index - 1]; + else if (b != !Streams[cp->Index - 1]) + return true; + + } // endif mode + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VECFAM::OpenColumnFile(PGLOBAL g, char *opmode, int i) + { + char filename[_MAX_PATH]; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + if (!(Streams[i] = PlugOpenFile(g, filename, opmode))) { + if (trace) + htrc("%s\n", g->Message); + + return (Tdbp->GetMode() == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Streams + + if (trace) + htrc("File %s is open in mode %s\n", filename, opmode); + + To_Fbs[i] = dup->Openlist; // Keep track of File blocks + return false; + } // end of OpenColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool VECFAM::AllocateBuffer(PGLOBAL g) + { + int i; + PVCTCOL cp; + PCOLDEF cdp; + PTDBVCT tdbp = (PTDBVCT)Tdbp; + MODE mode = tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)tdbp->GetDef(); + + if (mode != MODE_READ) { + // Allocate what is needed by all modes except Read + T_Streams = (FILE* *)PlugSubAlloc(g, NULL, Ncol * sizeof(FILE*)); + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + // Give default values + for (i = 0; i < Ncol; i++) { + T_Streams[i] = Streams[i]; + Clens[i] = 0; + } // endfor i + + } // endif mode + + if (mode == MODE_INSERT) { + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + To_Bufs = (void**)PlugSubAlloc(g, NULL, Ncol * sizeof(void*)); + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + To_Bufs[i] = PlugSubAlloc(g, NULL, Nrec * Clens[i]); + + if (cdp->GetType() == TYPE_STRING) + memset(To_Bufs[i], ' ', Nrec * Clens[i]); + else + memset(To_Bufs[i], 0, Nrec * Clens[i]); + + } // endfor cdp + + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, To_Bufs[cp->Index - 1], + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + return InitInsert(g); + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines and make Temp + if (UseTemp) { + Tempat = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + strcpy(Tempat, Colfn); + PlugSetPath(Tempat, Tempat, Tdbp->GetPath()); + strcat(PlugRemoveType(Tempat, Tempat), ".t"); + T_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + } // endif UseTemp + + if (UseTemp) + for (i = 0; i < Ncol; i++) { + T_Streams[i] = (mode == MODE_UPDATE) ? (FILE*)1 : NULL; + T_Fbs[i] = NULL; + } // endfor i + + if (mode == MODE_DELETE) { // All columns are moved + cdp = defp->GetCols(); + + for (i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) { + Clens[i] = cdp->GetClen(); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + } else { // Mode Update, only some columns are updated + for (cp = (PVCTCOL)tdbp->To_SetCols; cp; cp = (PVCTCOL)cp->Next) { + i = cp->Index -1; + + if (UseTemp) + T_Streams[i] = NULL; // Mark the streams to open + + Clens[i] = cp->Clen; + Buflen = max(Buflen, cp->Clen); + } // endfor cp + + InitUpdate = true; // To be initialized + } // endif mode + + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen * Nrec); + } // endif mode + + // Finally allocate column buffers for all modes + for (cp = (PVCTCOL)tdbp->Columns; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } // endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Do initial action when inserting. */ +/***********************************************************************/ +bool VECFAM::InitInsert(PGLOBAL g) + { + // We come here in MODE_INSERT only + CurBlk = 0; + CurNum = 0; + AddBlock = true; + return false; + } // end of InitInsert + +/***********************************************************************/ +/* Reset buffer access according to indexing and to mode. */ +/* >>>>>>>>>>>>>> TO BE RE-VISITED AND CHECKED <<<<<<<<<<<<<<<<<<<<<< */ +/***********************************************************************/ +void VECFAM::ResetBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* If access is random, performances can be much better when the */ + /* reads are done on only one row, except for small tables that can */ + /* be entirely read in one block. If the index is just used as a */ + /* bitmap filter, as for Update or Delete, reading will be */ + /* sequential and we better keep block reading. */ + /*********************************************************************/ + if (Tdbp->GetKindex() && Block > 1 && Tdbp->GetMode() == MODE_READ) { + Nrec = 1; // Better for random access + Rbuf = 0; + OldBlk = -2; // Has no meaning anymore + Block = Tdbp->Cardinality(g); // Blocks are one line now + Last = 1; // Probably unuseful + } // endif Mode + + } // end of ResetBuffer + +/***********************************************************************/ +/* Data Base write routine for VCT access method. */ +/***********************************************************************/ +int VECFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("VCT WriteBuffer: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_INSERT) { + if (Closing || ++CurNum == Nrec) { + // Here we must add a new blocks to the files + int i; + size_t n = (size_t)CurNum; + + for (i = 0; i < Ncol; i++) + if (n != fwrite(To_Bufs[i], (size_t)Clens[i], n, Streams[i])) { + sprintf(g->Message, MSG(WRITE_STRERROR), To_File, strerror(errno)); + return RC_FX; + } // endif + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif Closing || CurNum + + } else // Mode Update + // Writing updates being done in ReadDB we do initialization only. + if (InitUpdate) { + if (OpenTempFile(g)) + return RC_FX; + + InitUpdate = false; // Done + } // endif InitUpdate + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for split vertical access methods. */ +/* Note: lines are moved directly in the files (ooops...) */ +/***********************************************************************/ +int VECFAM::DeleteRecords(PGLOBAL g, int irc) + { + /*********************************************************************/ + /* There is an alternative here: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /* This depends on the Check setting, false by default. */ + /*********************************************************************/ + if (trace) + htrc("VEC DeleteDB: rc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = Cardinality(g); + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + // First line to delete + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary files, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Spos = Tpos = Fpos; + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + if (!UseTemp) { + /*****************************************************************/ + /* Because the chsize functionality is only accessible with a */ + /* system call we must close the file and reopen it with the */ + /* open function (_fopen for MS??) this is still to be checked */ + /* for compatibility with other OS's. */ + /*****************************************************************/ + char filename[_MAX_PATH]; + int h, rc; // File handle, return code + + for (int i = 0; i < Ncol; i++) { + sprintf(filename, Colfn, i + 1); + rc = PlugCloseFile(g, To_Fbs[i]); + + if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(UNIX) + if (ftruncate(h, (off_t)(Tpos * Clens[i]))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#else + if (chsize(h, Tpos * Clens[i])) { + sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno)); + close(h); + return RC_FX; + } // endif +#endif + + close(h); + + if (trace) + htrc("done, h=%d irc=%d\n", h, irc); + + } // endfor i + + } else // UseTemp + // Ok, now delete old files and rename new temp files + if (RenameTempFile(g) == RC_FX) + return RC_FX; + + // Reset these values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open temporary files used while updating or deleting. */ +/* Note: the files not updated have been given a T_Stream value of 1. */ +/***********************************************************************/ +bool VECFAM::OpenTempFile(PGLOBAL g) + { + char tempname[_MAX_PATH]; + + for (int i = 0; i < Ncol; i++) + if (!T_Streams[i]) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + sprintf(tempname, Tempat, i+1); + + if (!(T_Streams[i] = PlugOpenFile(g, tempname, "wb"))) { + if (trace) + htrc("%s\n", g->Message); + + return true; + } else + T_Fbs[i] = PlgGetUser(g)->Openlist; + + } else // This is a column that is not updated + T_Streams[i] = NULL; // For RenameTempFile + + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate updated lines before writing blocks. */ +/***********************************************************************/ +bool VECFAM::MoveLines(PGLOBAL g) + { + if (UseTemp && !InitUpdate) { // Don't do it in check pass + Fpos = OldBlk * Nrec; + + if (MoveIntermediateLines(g)) { + Closing = true; // ??? + return true; + } // endif UseTemp + +// Spos = Fpos + Nrec; + } // endif UseTemp + return false; + + } // end of MoveLines + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool VECFAM::MoveIntermediateLines(PGLOBAL g, bool *bn) + { + int i; + int n; + bool b = false; + size_t req, len; + + for (n = Fpos - Spos; n > 0; n -= Nrec) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + req = (size_t)min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + if (!T_Streams[i]) + continue; // Non updated column + + if (!UseTemp || !b) + if (fseek(Streams[i], Spos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno)); + return true; + } // endif + + len = fread(To_Buf, Clens[i], req, Streams[i]); + + if (trace) + htrc("after read req=%d len=%d\n", req, len); + + if (len != req) { + sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len); + return true; + } // endif len + + if (!UseTemp) + if (fseek(T_Streams[i], Tpos * Clens[i], SEEK_SET)) { + sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno)); + return true; + } // endif + + if ((len = fwrite(To_Buf, Clens[i], req, T_Streams[i])) != req) { + sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno)); + return true; + } // endif + + if (trace) + htrc("after write pos=%d\n", ftell(Streams[i])); + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + b = true; + } // endfor n + + return false; + } // end of MoveIntermediate Lines + +/***********************************************************************/ +/* Delete the old files and rename the new temporary files. */ +/***********************************************************************/ +int VECFAM::RenameTempFile(PGLOBAL g) + { + char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH]; + int rc = RC_OK; + + // Close all files. + // This loop is necessary because, in case of join, + // the table files can have been open several times. + for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next) + rc = PlugCloseFile(g, fb); + + for (int i = 0; i < Ncol && rc == RC_OK; i++) { + if (!T_Fbs[i]) + continue; + + tempname = (char*)T_Fbs[i]->Fname; + sprintf(filename, Colfn, i+1); + PlugSetPath(filename, filename, Tdbp->GetPath()); + strcat(PlugRemoveType(filetemp, filename), ".ttt"); + remove(filetemp); // May still be there from previous error + + if (rename(filename, filetemp)) { // Save file for security + sprintf(g->Message, MSG(RENAME_ERROR), + filename, filetemp, strerror(errno)); + rc = RC_FX; + } else if (rename(tempname, filename)) { + sprintf(g->Message, MSG(RENAME_ERROR), + tempname, filename, strerror(errno)); + rc = rename(filetemp, filename); // Restore saved file + rc = RC_FX; + } else if (remove(filetemp)) { + sprintf(g->Message, MSG(REMOVE_ERROR), + filetemp, strerror(errno)); + rc = RC_INFO; // Acceptable + } // endif's + + } // endfor i + + return rc; + } // end of RenameTempFile + +/***********************************************************************/ +/* Data Base close routine for VEC access method. */ +/***********************************************************************/ +void VECFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last += (CurBlk * Nrec + CurNum -1); + Block += (Last / Nrec); + Last = Last % Nrec + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Block += CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) + rc = ResetTableSize(g, Block, Last); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (mode == MODE_UPDATE) { + if (UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = OldBlk * Nrec; + wrc = MoveIntermediateLines(g); +// Spos = Fpos + Nrec; + } // endif UseTemp + + // Write back to file any pending modifications + if (wrc == RC_OK) + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (wrc == RC_OK && UseTemp && !InitUpdate) { + // Write any intermediate lines to temp file + Fpos = (Block - 1) * Nrec + Last; + wrc = MoveIntermediateLines(g); + } // endif UseTemp + + } // endif's mode + + if (UseTemp && !InitUpdate) { + // If they are errors, leave files unchanged + if (wrc == RC_OK) + rc = RenameTempFile(g); + else + longjmp(g->jumper[g->jump_level], 44); + + } else if (Streams) + for (int i = 0; i < Ncol; i++) + if (Streams[i]) { + rc = PlugCloseFile(g, To_Fbs[i]); + Streams[i] = NULL; + To_Fbs[i] = NULL; + } // endif Streams + + if (trace) + htrc("VCT CloseTableFile: closing %s wrc=%d rc=%d\n", To_File, wrc, rc); + + } // end of CloseTableFile + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool VECFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + len = Nrec * colp->Clen * CurBlk; + i = colp->Index - 1; + + if (trace) + htrc("len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d\n", + len, i, Nrec, colp->Deplac, Lrecl, CurBlk); + + if (fseek(Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + n = fread(colp->Blk->GetValPointer(), (size_t)colp->Clen, + (size_t)Nrec, Streams[i]); + + if (n != (size_t)Nrec && (CurBlk+1 != Block || n != (size_t)Last)) { + char fn[_MAX_PATH]; + + sprintf(fn, Colfn, colp->Index); +#if defined(WIN32) + if (feof(Streams[i])) +#else // !WIN32 + if (errno == NO_ERROR) +#endif // !WIN32 + sprintf(g->Message, MSG(BAD_READ_NUMBER), (int) n, fn); + else + sprintf(g->Message, MSG(READ_ERROR), + fn, strerror(errno)); + + if (trace) + htrc(" Read error: %s\n", g->Message); + + return true; + } // endif + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool VECFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int i, len; + size_t n; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + len = Nrec * colp->Clen * colp->ColBlk; + i = colp->Index - 1; + + if (trace) + htrc("modif=%d len=%d i=%d Nrec=%d Deplac=%d Lrecl=%d colblk=%d\n", + Modif, len, i, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (Tdbp->GetMode() == MODE_UPDATE && !UseTemp) + if (fseek(T_Streams[i], len, SEEK_SET)) { + sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno)); + return true; + } // endif + + // Here Nrec was changed to CurNum in mode Insert, + // this is the true number of records to write, + // this also avoid writing garbage in the file for true vector tables. + n = (Tdbp->GetMode() == MODE_INSERT) ? CurNum + : (colp->ColBlk == Block - 1) ? Last : Nrec; + + if (n != fwrite(colp->Blk->GetValPointer(), + (size_t)colp->Clen, n, T_Streams[i])) { + char fn[_MAX_PATH]; + + sprintf(fn, (UseTemp) ? Tempat : Colfn, colp->Index); + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("Write error: %s\n", strerror(errno)); + + return true; + } else + Spos = Fpos + n; + +#if defined(UNIX) + fflush(Streams[i]); //NGC +#endif + return false; + } // end of WriteBlock + +/* -------------------------- Class VMPFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the VMPFAM class. */ +/***********************************************************************/ +VMPFAM::VMPFAM(PVCTDEF tdp) : VCMFAM((PVCTDEF)tdp) + { + To_Fbs = NULL; + Split = true; + Block = Last = -1; + } // end of VMPFAM standard constructor + +VMPFAM::VMPFAM(PVMPFAM txfp) : VCMFAM(txfp) + { + To_Fbs = txfp->To_Fbs; + } // end of VMPFAM copy constructor + +/***********************************************************************/ +/* VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool VMPFAM::OpenTableFile(PGLOBAL g) + { + int i; + bool b; + MODE mode = Tdbp->GetMode(); + PCOLDEF cdp; + PVCTCOL cp; + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + if (mode == MODE_DELETE && !Tdbp->GetNext()) { + DelRows = Cardinality(g); + + // This will stop the process by causing GetProgMax to return 0. + ResetTableSize(g, 0, Nrec); + } else + Cardinality(g); // See comment in VECFAM::OpenTbleFile + + + /*********************************************************************/ + /* Prepare the filename pattern for column files and set Ncol. */ + /*********************************************************************/ + if (!Colfn) { + // Prepare the column file name pattern + Colfn = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + Ncol = ((VCTDEF*)Tdbp->GetDef())->MakeFnPattern(Colfn); + } // endif Colfn + + /*********************************************************************/ + /* Initialize the array of file structures. */ + /*********************************************************************/ + Memcol = (char* *)PlugSubAlloc(g, NULL, Ncol * sizeof(char *)); + To_Fbs = (PFBLOCK *)PlugSubAlloc(g, NULL, Ncol * sizeof(PFBLOCK)); + + for (i = 0; i < Ncol; i++) { + Memcol[i] = NULL; + To_Fbs[i] = NULL; + } // endif i + + /*********************************************************************/ + /* Open the files corresponding to columns used in the query. */ + /*********************************************************************/ + if (mode == MODE_DELETE) { + // All columns are used in Delete mode + for (i = 0, cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) + if (MapColumnFile(g, mode, i)) + return true; + + } else { + /*******************************************************************/ + /* Open the files corresponding updated columns of the query. */ + /*******************************************************************/ + for (cp = (PVCTCOL)((PTDBVCT)Tdbp)->To_SetCols; cp; + cp = (PVCTCOL)cp->Next) + if (MapColumnFile(g, MODE_UPDATE, cp->Index - 1)) + return true; + + /*******************************************************************/ + /* Open other non already open used columns (except pseudos) */ + /*******************************************************************/ + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial() && !Memcol[cp->Index - 1]) + if (MapColumnFile(g, MODE_READ, cp->Index - 1)) + return true; + + } // endif mode + + /*********************************************************************/ + /* Check for void table or missing columns */ + /*********************************************************************/ + for (b = !Memcol[0], i = 1; i < Ncol; i++) + if (b != !Memcol[i]) + return true; + + /*********************************************************************/ + /* Allocate the table and column block buffer. */ + /*********************************************************************/ + return (b) ? false : AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Open the file corresponding to one column. */ +/***********************************************************************/ +bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i) + { + char filename[_MAX_PATH]; + int len; + HANDLE hFile; + MEMMAP mm; + PFBLOCK fp; + PDBUSER dup = PlgGetUser(g); + + sprintf(filename, Colfn, i+1); + + /*********************************************************************/ + /* The whole file will be mapped so we can use it as */ + /* if it were entirely read into virtual memory. */ + /* Firstly we check whether this file have been already mapped. */ + /*********************************************************************/ + if (mode == MODE_READ) { + for (fp = dup->Openlist; fp; fp = fp->Next) + if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename) + && fp->Count && fp->Mode == mode) + break; + + if (trace) + htrc("Mapping file, fp=%p\n", fp); + + } else + fp = NULL; + + if (fp) { + /*******************************************************************/ + /* File already mapped. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Memcol[i] = fp->Memory; + len = fp->Length; + } else { + /*******************************************************************/ + /* Create the mapping file object. */ + /*******************************************************************/ + hFile = CreateFileMap(g, filename, &mm, mode, DelRows); + + if (hFile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + + if (!(*g->Message)) + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "map", (int) rc, filename); + if (trace) + htrc("%s\n", g->Message); + + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif hFile + + /*****************************************************************/ + /* Get the file size (assuming file is smaller than 4 GB) */ + /*****************************************************************/ + len = mm.lenL; + Memcol[i] = (char *)mm.memory; + + if (!len) { // Empty or deleted file + CloseFileHandle(hFile); + ResetTableSize(g, 0, Nrec); + return false; + } // endif len + + if (!Memcol[i]) { + CloseFileHandle(hFile); + sprintf(g->Message, MSG(MAP_VIEW_ERROR), + filename, GetLastError()); + return true; + } // endif Memory + + if (mode != MODE_DELETE) { + CloseFileHandle(hFile); // Not used anymore + hFile = INVALID_HANDLE_VALUE; // For Fblock + } // endif Mode + + /*******************************************************************/ + /* Link a Fblock. This make possible to reuse already opened maps */ + /* and also to automatically unmap them in case of error g->jump. */ + /* Note: block can already exist for previously closed file. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_MAP; + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy((char*)fp->Fname, filename); + fp->Next = dup->Openlist; + dup->Openlist = fp; + fp->Count = 1; + fp->Length = len; + fp->Memory = Memcol[i]; + fp->Mode = mode; + fp->File = NULL; + fp->Handle = hFile; // Used for Delete + } // endif fp + + To_Fbs[i] = fp; // Useful when closing + + if (trace) + htrc("fp=%p count=%d MapView=%p len=%d\n", + fp, fp->Count, Memcol[i], len); + + return false; + } // end of MapColumnFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/* Give a dummy value (1) to prevent allocating the value block. */ +/* It will be set pointing into the memory map of the file. */ +/***********************************************************************/ +bool VMPFAM::AllocateBuffer(PGLOBAL g) + { + PVCTCOL cp; + + if (Tdbp->GetMode() == MODE_DELETE) { + PCOLDEF cdp = Tdbp->GetDef()->GetCols(); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + for (int i = 0; cdp && i < Ncol; i++, cdp = cdp->GetNext()) + Clens[i] = cdp->GetClen(); + + } // endif mode + + for (cp = (PVCTCOL)Tdbp->GetColumns(); cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) { // Not a pseudo column + cp->Blk = AllocValBlock(g, (void*)1, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + cp->AddStatus(BUF_MAPPED); + } // endif IsSpecial + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base delete line routine for VMP access method. */ +/* Lines between deleted lines are moved in the mapfile view. */ +/***********************************************************************/ +int VMPFAM::DeleteRecords(PGLOBAL g, int irc) + { + int i; + int m, n; + + if (trace) + htrc("VMP DeleteDB: irc=%d tobuf=%p Tpos=%p Spos=%p\n", + irc, To_Buf, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the top of map position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file top=%p\n", Fpos); + + } else // Fpos is the Deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) + /*******************************************************************/ + /* First line to delete. Move of eventual preceeding lines is */ + /* not required here, just setting of future Spos and Tpos. */ + /*******************************************************************/ + Tpos = Fpos; // Spos is set below + else if ((n = Fpos - Spos) > 0) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + for (i = 0; i < Ncol; i++) { + m = Clens[i]; + memmove(Memcol[i] + Tpos * m, Memcol[i] + Spos * m, m * n); + } // endif i + + Tpos += n; + + if (trace) + htrc("move %d bytes\n", n); + + } // endif n + + if (irc == RC_OK) { + Spos = Fpos + 1; // New start position + + if (trace) + htrc("after: Tpos=%p Spos=%p\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /* We must firstly Unmap the view and use the saved file handle */ + /* to put an EOF at the end of the copied part of the file. */ + /*******************************************************************/ + PFBLOCK fp; + + for (i = 0; i < Ncol; i++) { + fp = To_Fbs[i]; + CloseMemMap(fp->Memory, (size_t)fp->Length); + fp->Count = 0; // Avoid doing it twice + + /*****************************************************************/ + /* Remove extra records. */ + /*****************************************************************/ + n = Tpos * Clens[i]; + +#if defined(WIN32) + DWORD drc = SetFilePointer(fp->Handle, n, NULL, FILE_BEGIN); + + if (drc == 0xFFFFFFFF) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetFilePointer", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + if (trace) + htrc("done, Tpos=%p newsize=%d drc=%d\n", Tpos, n, drc); + + if (!SetEndOfFile(fp->Handle)) { + sprintf(g->Message, MSG(FUNCTION_ERROR), + "SetEndOfFile", GetLastError()); + CloseHandle(fp->Handle); + return RC_FX; + } // endif + + CloseHandle(fp->Handle); +#else // UNIX + if (ftruncate(fp->Handle, (off_t)n)) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + close(fp->Handle); + return RC_FX; + } // endif + + close(fp->Handle); +#endif // UNIX + } // endfor i + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for VMP access method. */ +/***********************************************************************/ +void VMPFAM::CloseTableFile(PGLOBAL g) + { + if (Tdbp->GetMode() == MODE_DELETE) { + // Set Block and Nrec values for TDBVCT::MakeBlockValues + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + ResetTableSize(g, Block, Last); + } else if (Tdbp->GetMode() == MODE_INSERT) + assert(false); + + for (int i = 0; i < Ncol; i++) + PlugCloseFile(g, To_Fbs[i]); + + } // end of CloseTableFile + +/* -------------------------- Class BGVFAM --------------------------- */ + +/***********************************************************************/ +/* Implementation of the BGVFAM class. */ +/***********************************************************************/ +// Constructors +BGVFAM::BGVFAM(PVCTDEF tdp) : VCTFAM(tdp) + { + Hfile = INVALID_HANDLE_VALUE; + Tfile = INVALID_HANDLE_VALUE; + BigDep = NULL; + } // end of BGVFAM constructor + +BGVFAM::BGVFAM(PBGVFAM txfp) : VCTFAM(txfp) + { + Hfile = txfp->Hfile; + Tfile = txfp->Tfile; + BigDep= txfp->BigDep; + } // end of BGVFAM copy constructor + +/***********************************************************************/ +/* Set current position in a big file. */ +/***********************************************************************/ +bool BGVFAM::BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b) + { +#if defined(WIN32) + char buf[256]; + DWORD drc, m = (b) ? FILE_END : FILE_BEGIN; + LARGE_INTEGER of; + + of.QuadPart = pos; + of.LowPart = SetFilePointer(h, of.LowPart, &of.HighPart, m); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (drc = GetLastError()) != NO_ERROR) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(SFP_ERROR), buf); + return true; + } // endif +#else // !WIN32 + if (lseek64(h, pos, (b) ? SEEK_END : SEEK_SET) < 0) { + sprintf(g->Message, MSG(ERROR_IN_LSK), errno); + return true; + } // endif +#endif // !WIN32 + + return false; + } // end of BigSeek + +/***********************************************************************/ +/* Read from a big file. */ +/***********************************************************************/ +bool BGVFAM::BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbr, drc, len = (DWORD)req; + bool brc = ReadFile(h, inbuf, len, &nbr, NULL); + + if (trace) + htrc("after read req=%d brc=%d nbr=%d\n", req, brc, nbr); + + if (!brc || nbr != len) { + char buf[256]; // , *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_READ)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(READ_ERROR), To_File, buf); + + if (trace) + htrc("BIGREAD: %s\n", g->Message); + + rc = true; + } // endif brc || nbr +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbr = read(h, inbuf, len); + + if (nbr != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(READ_ERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGREAD: nbr=%d len=%d errno=%d %s\n", + nbr, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigRead + +/***********************************************************************/ +/* Write into a big file. */ +/***********************************************************************/ +bool BGVFAM::BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req) + { + bool rc = false; + +#if defined(WIN32) + DWORD nbw, drc, len = (DWORD)req; + bool brc = WriteFile(h, inbuf, len, &nbw, NULL); + + if (trace) + htrc("after write req=%d brc=%d nbw=%d\n", req, brc, nbw); + + if (!brc || nbw != len) { + char buf[256], *fn = (h == Hfile) ? To_File : "Tempfile"; + + if (brc) + strcpy(buf, MSG(BAD_BYTE_NUM)); + else { + drc = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + } // endelse brc + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, buf); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, drc, g->Message); + + rc = true; + } // endif brc || nbw +#else // !WIN32 + size_t len = (size_t)req; + ssize_t nbw = write(h, inbuf, len); + + if (nbw != (ssize_t)len) { + const char *fn = (h == Hfile) ? To_File : "Tempfile"; + + sprintf(g->Message, MSG(WRITE_STRERROR), fn, strerror(errno)); + + if (trace) + htrc("BIGWRITE: nbw=%d len=%d errno=%d %s\n", + nbw, len, errno, g->Message); + + rc = true; + } // endif nbr +#endif // !WIN32 + + return rc; + } // end of BigWrite + +/***********************************************************************/ +/* Get the Headlen, Block and Last info from the file header. */ +/***********************************************************************/ +int BGVFAM::GetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + int n; + bool b; + VECHEADER vh; + HANDLE h; + + if (Header < 1 || Header > 3 || !MaxBlk) { + sprintf(g->Message, "Invalid header value %d", Header); + return -1; + } else + n = (Header == 1) ? (int)sizeof(VECHEADER) : 0; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header == 2) + strcat(PlugRemoveType(filename, filename), ".blk"); + +#if defined(WIN32) + LARGE_INTEGER len; + + h = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h != INVALID_HANDLE_VALUE) { + // Get the size of the file (can be greater than 4 GB) + len.LowPart = GetFileSize(h, (LPDWORD)&len.HighPart); + } // endif h + + if (h == INVALID_HANDLE_VALUE || !len.QuadPart) { +#else // !WIN32 + h = open64(filename, O_RDONLY, 0); + + if (h == INVALID_HANDLE_VALUE || !_filelength(h)) { +#endif // !WIN32 + // Consider this is a void table + if (trace) + htrc("Void table h=%d\n", h); + + Last = Nrec; + Block = 0; + + if (h != INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return n; + } else if (Header == 3) + b = BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + if (BigRead(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error reading header file %s", filename); + n = -1; + } else if (MaxBlk * Nrec != vh.MaxRec) { + sprintf(g->Message, "MaxRec=%d doesn't match MaxBlk=%d Nrec=%d", + vh.MaxRec, MaxBlk, Nrec); + n = -1; + } else { + Block = (vh.NumRec > 0) ? (vh.NumRec + Nrec - 1) / Nrec : 0; + Last = (vh.NumRec + Nrec - 1) % Nrec + 1; + + if (trace) + htrc("Block=%d Last=%d\n", Block, Last); + + } // endif's + + CloseFileHandle(h); + return n; + } // end of GetBlockInfo + +/***********************************************************************/ +/* Set the MaxRec and NumRec info in the file header. */ +/***********************************************************************/ +bool BGVFAM::SetBlockInfo(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool bk, b = false, rc = false; + VECHEADER vh; + HANDLE h = INVALID_HANDLE_VALUE; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (Header != 2) { + if (Hfile != INVALID_HANDLE_VALUE) { + h = Hfile; + + if (Header == 1) + bk = BigSeek(g, h, (BIGINT)0); + + } else + b = true; + + } else // Header == 2 + strcat(PlugRemoveType(filename, filename), ".blk"); + + if (h == INVALID_HANDLE_VALUE) { +#if defined(WIN32) + DWORD creation = (b) ? OPEN_EXISTING : TRUNCATE_EXISTING; + + h = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + +#else // !WIN32 + int oflag = (b) ? O_RDWR : O_RDWR | O_TRUNC; + + h = open64(filename, oflag, 0); +#endif // !WIN32 + + if (h == INVALID_HANDLE_VALUE) { + sprintf(g->Message, "Error opening header file %s", filename); + return true; + } // endif h + + } // endif h + + if (Header == 3) + bk = BigSeek(g, h, -(BIGINT)sizeof(vh), true); + + vh.MaxRec = MaxBlk * Bsize; + vh.NumRec = (Block - 1) * Nrec + Last; + + if (BigWrite(g, h, &vh, sizeof(vh))) { + sprintf(g->Message, "Error writing header file %s", filename); + rc = true; + } // endif fread + + if (Header == 2 || Hfile == INVALID_HANDLE_VALUE) + CloseFileHandle(h); + + return rc; + } // end of SetBlockInfo + +/***********************************************************************/ +/* VEC Create an empty file for new Vector formatted tables. */ +/***********************************************************************/ +bool BGVFAM::MakeEmptyFile(PGLOBAL g, char *fn) + { + // Vector formatted file this will create an empty file of the + // required length if it does not exists yet. + char filename[_MAX_PATH], c = 0; + int n = (Header == 1 || Header == 3) ? sizeof(VECHEADER) : 0; + + PlugSetPath(filename, fn, Tdbp->GetPath()); + +#if defined(WIN32) + char *p; + DWORD rc; + bool brc; + LARGE_INTEGER of; + HANDLE h; + + h = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) { + p = MSG(OPENING); + goto err; + } // endif h + + of.QuadPart = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: of=%lld n=%d maxblk=%d blksize=%d\n", + of.QuadPart, n, MaxBlk, Blksize); + + of.LowPart = SetFilePointer(h, of.LowPart, + &of.HighPart, FILE_BEGIN); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) { + p = MSG(MAKING); + goto err; + } // endif + + brc = WriteFile(h, &c, 1, &rc, NULL); + + if (!brc || rc != 1) { + p = MSG(WRITING); + goto err; + } // endif + + CloseHandle(h); + return false; + + err: + rc = GetLastError(); + sprintf(g->Message, MSG(EMPTY_FILE), p, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + + if (h != INVALID_HANDLE_VALUE) + CloseHandle(h); + + return true; +#else // !WIN32 + int h; + BIGINT pos; + + h= open64(filename, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); + + if (h == -1) + return true; + + pos = (BIGINT)n + (BIGINT)MaxBlk * (BIGINT)Blksize - (BIGINT)1; + + if (trace) + htrc("MEF: pos=%lld n=%d maxblk=%d blksize=%d\n", + pos, n, MaxBlk, Blksize); + + if (lseek64(h, pos, SEEK_SET) < 0) { + sprintf(g->Message, MSG(MAKE_EMPTY_FILE), To_File, strerror(errno)); + close(h); + return true; + } // endif h + + write(h, &c, 1); // This actually fills the empty file + close(h); + return false; +#endif // !WIN32 + } // end of MakeEmptyFile + +/***********************************************************************/ +/* Vopen function: opens a file using Windows or Unix API's. */ +/***********************************************************************/ +bool BGVFAM::OpenTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool del = false; + MODE mode = Tdbp->GetMode(); + PDBUSER dbuserp = PlgGetUser(g); + + if ((To_Fb && To_Fb->Count) || Hfile != INVALID_HANDLE_VALUE) { + sprintf(g->Message, MSG(FILE_OPEN_YET), To_File); + return true; + } // endif + + /*********************************************************************/ + /* Update block info if necessary. */ + /*********************************************************************/ + if (Block < 0) + if ((Headlen = GetBlockInfo(g)) < 0) + return true; + + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (trace) + htrc("OpenTableFile: filename=%s mode=%d Last=%d\n", + filename, mode, Last); + +#if defined(WIN32) + DWORD access, creation, share = 0, rc = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + creation = OPEN_EXISTING; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + access = GENERIC_READ | GENERIC_WRITE; + } else if (Last == Nrec) + access = GENERIC_WRITE; + else + // Required to update the last block + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_ALWAYS; + break; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will stop the process by + // causing GetProgMax to return 0. +// ResetTableSize(g, 0, Nrec); must be done later + del = true; + + // This will delete the whole file + access = GENERIC_READ | GENERIC_WRITE; + creation = TRUNCATE_EXISTING; + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + if ((UseTemp = Tdbp->IsUsingTemp(g))) + access = GENERIC_READ; + else + access = GENERIC_READ | GENERIC_WRITE; + + creation = OPEN_EXISTING; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + /*********************************************************************/ + /* Use specific Windows API functions. */ + /*********************************************************************/ + Hfile = CreateFile(filename, access, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + } // endif Hfile + + if (trace) + htrc(" rc=%d access=%p share=%p creation=%d handle=%p fn=%s\n", + rc, access, share, creation, Hfile, filename); + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode we must position the cursor at end of file. */ + /*******************************************************************/ + LARGE_INTEGER of; + + of.QuadPart = (BIGINT)0; + of.LowPart = SetFilePointer(Hfile, of.LowPart, + &of.HighPart, FILE_END); + + if (of.LowPart == INVALID_SET_FILE_POINTER && + (rc = GetLastError()) != NO_ERROR) { + sprintf(g->Message, MSG(ERROR_IN_SFP), rc); + CloseHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + } // endif + + } // endif Mode + +#else // UNIX + /*********************************************************************/ + /* The open() function has a transitional interface for 64-bit */ + /* file offsets. Note that using open64() is equivalent to using */ + /* open() with O_LARGEFILE set in oflag (see Xopen in tabfix.cpp). */ + /*********************************************************************/ + int rc = 0; + int oflag; + mode_t pmd = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + oflag = O_RDONLY; + break; + case MODE_INSERT: + if (MaxBlk) { + if (!Block) + if (MakeEmptyFile(g, To_File)) + return true; + + // Required to update empty blocks + oflag = O_RDWR; + } else if (Last == Nrec) + oflag = O_WRONLY | O_CREAT | O_APPEND; + else + // Required to update the last block + oflag = O_RDWR | O_CREAT | O_APPEND; + + pmd = S_IREAD | S_IWRITE; + break; + case MODE_DELETE: + // This is temporary until a partial delete is implemented + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + del = true; + + // This will delete the whole file and provoque ReadDB to + // return immediately. + oflag = O_RDWR | O_TRUNC; + strcpy(g->Message, MSG(NO_VCT_DELETE)); + break; + } // endif + + // Selective delete, pass thru + case MODE_UPDATE: + UseTemp = Tdbp->IsUsingTemp(g); + oflag = (UseTemp) ? O_RDONLY : O_RDWR; + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch + + Hfile = open64(filename, oflag, pmd); // Enable file size > 2G + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + strcat(g->Message, strerror(errno)); + } // endif Hfile + + if (trace) + htrc(" rc=%d oflag=%p mode=%p handle=%d fn=%s\n", + rc, oflag, mode, Hfile, filename); +#endif // UNIX + + if (!rc) { + if (!To_Fb) { + To_Fb = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fb->Fname = To_File; + To_Fb->Type = TYPE_FB_HANDLE; + To_Fb->Memory = NULL; + To_Fb->Length = 0; + To_Fb->File = NULL; + To_Fb->Next = dbuserp->Openlist; + dbuserp->Openlist = To_Fb; + } // endif To_Fb + + To_Fb->Count = 1; + To_Fb->Mode = mode; + To_Fb->Handle = Hfile; + + if (trace) + htrc("File %s is open in mode %d\n", filename, mode); + + if (del) + // This will stop the process by + // causing GetProgMax to return 0. + return ResetTableSize(g, 0, Nrec); + + /*********************************************************************/ + /* Allocate the table and column block buffers. */ + /*********************************************************************/ + return AllocateBuffer(g); + } else + return (mode == MODE_READ && rc == ENOENT) + ? PushWarning(g, Tdbp) : true; + + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the block buffers for columns used in the query. */ +/***********************************************************************/ +bool BGVFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCOLDEF cdp; + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (mode == MODE_INSERT) { + if (!NewBlock) { + // Not reopening after inserting the last block + bool chk = PlgGetUser(g)->Check & CHK_TYPE; + + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + memset(NewBlock + Nrec * cdp->GetPoff(), + (IsTypeNum(cdp->GetType()) ? 0 : ' '), + Nrec * cdp->GetClen()); + + for (; cp; cp = (PVCTCOL)cp->Next) + cp->Blk = AllocValBlock(g, NewBlock + Nrec * cp->Deplac, + cp->Buf_Type, Nrec, cp->Format.Length, + cp->Format.Prec, chk); + + InitInsert(g); // Initialize inserting + + // Currently we don't use a temporary file for inserting + Tfile = Hfile; + } // endif NewBlock + + } else { + if (UseTemp || mode == MODE_DELETE) { + // Allocate all that is needed to move lines + int i = 0; + + if (!Ncol) + for (cdp = defp->GetCols(); cdp; cdp = cdp->GetNext()) + Ncol++; + + if (MaxBlk) + BigDep = (BIGINT*)PlugSubAlloc(g, NULL, Ncol * sizeof(BIGINT)); + else + Deplac = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + + Clens = (int*)PlugSubAlloc(g, NULL, Ncol * sizeof(int)); + Isnum = (bool*)PlugSubAlloc(g, NULL, Ncol * sizeof(bool)); + + for (cdp = defp->GetCols(); cdp; i++, cdp = cdp->GetNext()) { + if (MaxBlk) + BigDep[i] = (BIGINT)Headlen + + (BIGINT)(cdp->GetPoff() * Nrec) * (BIGINT)MaxBlk; + else + Deplac[i] = cdp->GetPoff() * Nrec; + + Clens[i] = cdp->GetClen(); + Isnum[i] = IsTypeNum(cdp->GetType()); + Buflen = max(Buflen, cdp->GetClen()); + } // endfor cdp + + if (!UseTemp || MaxBlk) { + Buflen *= Nrec; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + } else + NewBlock = (char*)PlugSubAlloc(g, NULL, Blksize); + + } // endif mode + + for (; cp; cp = (PVCTCOL)cp->Next) + if (!cp->IsSpecial()) // Not a pseudo column + cp->Blk = AllocValBlock(g, NULL, cp->Buf_Type, Nrec, + cp->Format.Length, cp->Format.Prec); + + } //endif mode + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* Data Base write routine for huge VCT access method. */ +/***********************************************************************/ +int BGVFAM::WriteBuffer(PGLOBAL g) + { + if (trace) + htrc("BGV WriteDB: R%d Mode=%d CurNum=%d CurBlk=%d\n", + Tdbp->GetTdb_No(), Tdbp->GetMode(), CurNum, CurBlk); + + if (Tdbp->GetMode() == MODE_UPDATE) { + // Mode Update is done in ReadDB, we just initialize it here + if (Tfile == INVALID_HANDLE_VALUE) { + if (UseTemp) { + if (OpenTempFile(g)) + return RC_FX; + + // Most of the time, not all table columns are updated. + // This why we must completely pre-fill the temporary file. + Fpos = (MaxBlk) ? (Block - 1) * Nrec + Last + : Block * Nrec; // To write last lock + + if (MoveIntermediateLines(g)) + return RC_FX; + + } else + Tfile = Hfile; + + } // endif Tfile + + } else { + // Mode Insert + if (MaxBlk && CurBlk == MaxBlk) { + strcpy(g->Message, MSG(TRUNC_BY_ESTIM)); + return RC_EF; // Too many lines for a Vector formatted table + } // endif MaxBlk + + if (Closing || ++CurNum == Nrec) { + PVCTCOL cp = (PVCTCOL)Tdbp->GetColumns(); + + if (!AddBlock) { + // Write back the updated last block values + for (; cp; cp = (PVCTCOL)cp->Next) + cp->WriteBlock(g); + + if (!Closing && !MaxBlk) { + // Close the VCT file and reopen it in mode Insert +//#if defined(WIN32) //OB +// CloseHandle(Hfile); +//#else // UNIX +// close(Hfile); +//#endif // UNIX + CloseFileHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + To_Fb->Count = 0; + Last = Nrec; // Tested in OpenTableFile + + if (OpenTableFile(g)) { + Closing = true; // Tell CloseDB of error + return RC_FX; + } // endif Vopen + + AddBlock = true; + } // endif Closing + + } else { + // Here we must add a new block to the VCT file + if (Closing) + // Reset the overwritten columns for last block extra records + for (; cp; cp = (PVCTCOL)cp->Next) + memset(NewBlock + Nrec * cp->Deplac + Last * cp->Clen, + (cp->Buf_Type == TYPE_STRING) ? ' ' : '\0', + (Nrec - Last) * cp->Clen); + + if (BigWrite(g, Hfile, NewBlock, Blksize)) + return RC_FX; + + } // endif AddBlock + + if (!Closing) { + CurBlk++; + CurNum = 0; + } // endif Closing + + } // endif + + } // endif Mode + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for BGVFAM access method. */ +/***********************************************************************/ +int BGVFAM::DeleteRecords(PGLOBAL g, int irc) + { + bool eof = false; + + /*********************************************************************/ + /* There is an alternative here depending on UseTemp: */ + /* 1 - use a temporary file in which are copied all not deleted */ + /* lines, at the end the original file will be deleted and */ + /* the temporary file renamed to the original file name. */ + /* 2 - directly move the not deleted lines inside the original */ + /* file, and at the end erase all trailing records. */ + /*********************************************************************/ + if (trace) + htrc("BGV DeleteDB: irc=%d UseTemp=%d Fpos=%d Tpos=%d Spos=%d\n", + irc, UseTemp, Fpos, Tpos, Spos); + + if (irc != RC_OK) { + /*******************************************************************/ + /* EOF: position Fpos at the end-of-file position. */ + /*******************************************************************/ + Fpos = (Block - 1) * Nrec + Last; + + if (trace) + htrc("Fpos placed at file end=%d\n", Fpos); + + eof = UseTemp && !MaxBlk; + } else // Fpos is the deleted line position + Fpos = CurBlk * Nrec + CurNum; + + if (Tpos == Spos) { + if (UseTemp) { + /*****************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*****************************************************************/ + if (OpenTempFile(g)) + return RC_FX; + + } else { + /*****************************************************************/ + /* Move of eventual preceeding lines is not required here. */ + /* Set the target file as being the source file itself. */ + /* Set the future Tpos, and give Spos a value to block copying. */ + /*****************************************************************/ + Tfile = Hfile; + Spos = Tpos = Fpos; + } // endif UseTemp + + } // endif Tpos == Spos + + /*********************************************************************/ + /* Move any intermediate lines. */ + /*********************************************************************/ + if (MoveIntermediateLines(g, &eof)) + return RC_FX; + + if (irc == RC_OK) { +#ifdef _DEBUG + assert(Spos == Fpos); +#endif + Spos++; // New start position is on next line + + if (trace) + htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos); + + } else { + /*******************************************************************/ + /* Last call after EOF has been reached. */ + /*******************************************************************/ + Block = (Tpos > 0) ? (Tpos + Nrec - 1) / Nrec : 0; + Last = (Tpos + Nrec - 1) % Nrec + 1; + + if (!UseTemp) { // The UseTemp case is treated in CloseTableFile + if (!MaxBlk) { + if (Last < Nrec) // Clean last block + if (CleanUnusedSpace(g)) + return RC_FX; + + /***************************************************************/ + /* Remove extra records. */ + /***************************************************************/ +#if defined(WIN32) + BIGINT pos = (BIGINT)Block * (BIGINT)Blksize; + + if (BigSeek(g, Hfile, pos)) + return RC_FX; + + if (!SetEndOfFile(Hfile)) { + DWORD drc = GetLastError(); + + sprintf(g->Message, MSG(SETEOF_ERROR), drc); + return RC_FX; + } // endif error +#else // !WIN32 + if (ftruncate64(Hfile, (BIGINT)(Tpos * Lrecl))) { + sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno)); + return RC_FX; + } // endif +#endif // !WIN32 + } else // MaxBlk + // Clean the unused space in the file, this is required when + // inserting again with a partial column list. + if (CleanUnusedSpace(g)) + return RC_FX; + + if (ResetTableSize(g, Block, Last)) + return RC_FX; + + } // endif UseTemp + + } // endif irc + + return RC_OK; // All is correct + } // end of DeleteRecords + +/***********************************************************************/ +/* Open a temporary file used while updating or deleting. */ +/***********************************************************************/ +bool BGVFAM::OpenTempFile(PGLOBAL g) + { + char *tempname; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Open the temporary file, Spos is at the beginning of file. */ + /*********************************************************************/ + tempname = (char*)PlugSubAlloc(g, NULL, _MAX_PATH); + PlugSetPath(tempname, To_File, Tdbp->GetPath()); + strcat(PlugRemoveType(tempname, tempname), ".t"); + + if (!MaxBlk) + remove(tempname); // Be sure it does not exist yet + else if (MakeEmptyFile(g, tempname)) + return true; + +#if defined(WIN32) + DWORD access = (MaxBlk) ? OPEN_EXISTING : CREATE_NEW; + + Tfile = CreateFile(tempname, GENERIC_WRITE, 0, NULL, + access, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Tfile == INVALID_HANDLE_VALUE) { + DWORD rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_DELETE, tempname); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)tempname, _MAX_PATH, NULL); + strcat(g->Message, tempname); + return true; + } // endif Tfile +#else // UNIX + int oflag = (MaxBlk) ? O_WRONLY : O_WRONLY | O_TRUNC; + + Tfile = open64(tempname, oflag, S_IWRITE); + + if (Tfile == INVALID_HANDLE_VALUE) { + int rc = errno; + sprintf(g->Message, MSG(OPEN_ERROR), rc, MODE_INSERT, tempname); + strcat(g->Message, strerror(errno)); + return true; + } //endif Tfile +#endif // UNIX + + To_Fbt = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + To_Fbt->Fname = tempname; + To_Fbt->Type = TYPE_FB_HANDLE; + To_Fbt->Memory = NULL; + To_Fbt->Length = 0; + To_Fbt->File = NULL; + To_Fbt->Next = dup->Openlist; + To_Fbt->Count = 1; + To_Fbt->Mode = MODE_INSERT; + To_Fbt->Handle = Tfile; + dup->Openlist = To_Fbt; + return false; + } // end of OpenTempFile + +/***********************************************************************/ +/* Move intermediate deleted or updated lines. */ +/***********************************************************************/ +bool BGVFAM::MoveIntermediateLines(PGLOBAL g, bool *b) + { + int i, n, req, dep; + bool eof = (b) ? *b : false; + BIGINT pos; + + for (n = Fpos - Spos; n > 0 || eof; n -= req) { + /*******************************************************************/ + /* Non consecutive line to delete. Move intermediate lines. */ + /*******************************************************************/ + if (!MaxBlk) + req = (DWORD)min(n, Nrec - max(Spos % Nrec, Tpos % Nrec)); + else + req = (DWORD)min(n, Nrec); + + if (req) for (i = 0; i < Ncol; i++) { + if (!MaxBlk) { + if (UseTemp) + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + + pos = (BIGINT)Deplac[i] + (BIGINT)((Spos % Nrec) * Clens[i]) + + (BIGINT)(Spos / Nrec) * (BIGINT)Blksize; + } else + pos = BigDep[i] + (BIGINT)Spos * (BIGINT)Clens[i]; + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, To_Buf, req * Clens[i])) + return true; + + if (!UseTemp || MaxBlk) { + if (!MaxBlk) + pos = (BIGINT)Deplac[i] + (BIGINT)((Tpos % Nrec) * Clens[i]) + + (BIGINT)(Tpos / Nrec) * (BIGINT)Blksize; + else + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endif UseTemp + + } // endfor i + + Tpos += (int)req; + Spos += (int)req; + + if (UseTemp && !MaxBlk && (!(Tpos % Nrec) || (eof && Spos == Fpos))) { + // Write the full or last block to the temporary file + if ((dep = Nrec - (Tpos % Nrec)) < Nrec) + // Clean the last block in case of future insert, must be + // done here because Tfile was open in write only. + for (i = 0; i < Ncol; i++) { + To_Buf = NewBlock + Deplac[i] + (Tpos % Nrec) * Clens[i]; + memset(To_Buf, (Isnum[i]) ? 0 : ' ', dep * Clens[i]); + } // endfor i + + if (BigWrite(g, Tfile, NewBlock, Blksize)) + return true; + + if (Spos == Fpos) + eof = false; + + } // endif Usetemp... + + if (trace) + htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos); + + } // endfor n + + return false; + } // end of MoveIntermediateLines + +/***********************************************************************/ +/* Clean deleted space in a huge VCT or Vec table file. */ +/***********************************************************************/ +bool BGVFAM::CleanUnusedSpace(PGLOBAL g) + { + int i; + int n; + BIGINT pos, dep; + + if (!MaxBlk) { + /*******************************************************************/ + /* Clean last block of the VCT table file. */ + /*******************************************************************/ + assert(!UseTemp); // This case is handled in MoveIntermediateLines + + if (!(n = Nrec - Last)) + return false; + + dep = (BIGINT)((Block - 1) * Blksize); + + for (i = 0; i < Ncol; i++) { + memset(To_Buf, (Isnum[i]) ? 0 : ' ', n * Clens[i]); + pos = dep + (BIGINT)(Deplac[i] + Last * Clens[i]); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigWrite(g, Hfile, To_Buf, n * Clens[i])) + return true; + + } // endfor i + + } else { + int req; + + memset(To_Buf, 0, Buflen); + + for (n = Fpos - Tpos; n > 0; n -= req) { + /*****************************************************************/ + /* Fill VEC file remaining lines with 0's. */ + /* This seems to work even column blocks have been made with */ + /* Blanks = true. Perhaps should it be set to false for VEC. */ + /*****************************************************************/ + req = min(n, Nrec); + + for (i = 0; i < Ncol; i++) { + pos = BigDep[i] + (BIGINT)Tpos * (BIGINT)Clens[i]; + + if (BigSeek(g, Tfile, pos)) + return true; + + if (BigWrite(g, Tfile, To_Buf, req * Clens[i])) + return true; + + } // endfor i + + Tpos += req; + } // endfor n + + } // endif MaxBlk + + return false; + } // end of CleanUnusedSpace + +/***********************************************************************/ +/* Data Base close routine for huge VEC access method. */ +/***********************************************************************/ +void BGVFAM::CloseTableFile(PGLOBAL g) + { + int rc = 0, wrc = RC_OK; + MODE mode = Tdbp->GetMode(); + + if (mode == MODE_INSERT) { + if (Closing) + wrc = RC_FX; // Last write was in error + else + if (CurNum) { + // Some more inserted lines remain to be written + Last = CurNum; + Block = CurBlk + 1; + Closing = true; + wrc = WriteBuffer(g); + } else { + Last = Nrec; + Block = CurBlk; + wrc = RC_OK; + } // endif CurNum + + if (wrc != RC_FX) { + rc = ResetTableSize(g, Block, Last); + } else if (AddBlock) { + // Last block was not written + rc = ResetTableSize(g, CurBlk, Nrec); + longjmp(g->jumper[g->jump_level], 44); + } // endif + + } else if (mode == MODE_UPDATE) { + // Write back to file any pending modifications + for (PVCTCOL colp = (PVCTCOL)((PTDBVCT)Tdbp)->GetSetCols(); + colp; colp = (PVCTCOL)colp->Next) + colp->WriteBlock(g); + + if (UseTemp && Tfile) { + rc = RenameTempFile(g); + Hfile = Tfile = INVALID_HANDLE_VALUE; + + if (Header) + // Header must be set because it was not set in temp file + rc = SetBlockInfo(g); + + } // endif UseTemp + + } else if (mode == MODE_DELETE && UseTemp && Tfile) { + if (MaxBlk) + rc = CleanUnusedSpace(g); + + if ((rc = RenameTempFile(g)) != RC_FX) { + Hfile = Tfile = INVALID_HANDLE_VALUE; // For SetBlockInfo + rc = ResetTableSize(g, Block, Last); + } // endif rc + + } // endif's mode + + if (Hfile != INVALID_HANDLE_VALUE) + rc = PlugCloseFile(g, To_Fb); + + if (trace) + htrc("BGV CloseTableFile: closing %s wrc=%d rc=%d\n", + To_File, wrc, rc); + + Hfile = INVALID_HANDLE_VALUE; + } // end of CloseDB + +/***********************************************************************/ +/* Rewind routine for huge VCT access method. */ +/***********************************************************************/ +void BGVFAM::Rewind(void) + { + // In mode update we need to read Set Column blocks + if (Tdbp->GetMode() == MODE_UPDATE) + OldBlk = -1; + + // Initialize so block optimization is called for 1st block + CurBlk = -1; + CurNum = Nrec - 1; + +#if 0 // This is probably unuseful as the file is directly accessed +#if defined(WIN32) //OB + SetFilePointer(Hfile, 0, NULL, FILE_BEGIN); +#else // UNIX + lseek64(Hfile, 0, SEEK_SET); +#endif // UNIX +#endif // 0 + } // end of Rewind + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +bool BGVFAM::ReadBlock(PGLOBAL g, PVCTCOL colp) + { + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to read. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)CurBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)CurBlk); + + if (trace) + htrc("RB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d CurBlk=%d MaxBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, CurBlk, MaxBlk); + + if (BigSeek(g, Hfile, pos)) + return true; + + if (BigRead(g, Hfile, colp->Blk->GetValPointer(), colp->Clen * Nrec)) + return true; + + if (trace) + num_read++; + + return false; + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +bool BGVFAM::WriteBlock(PGLOBAL g, PVCTCOL colp) + { + int len; + BIGINT pos; + + /*********************************************************************/ + /* Calculate the offset and size of the block to write. */ + /*********************************************************************/ + if (MaxBlk) // File has Vector format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac * (BIGINT)MaxBlk + + (BIGINT)colp->Clen * (BIGINT)colp->ColBlk) + (BIGINT)Headlen; + else // Old VCT format + pos = (BIGINT)Nrec * ((BIGINT)colp->Deplac + + (BIGINT)Lrecl * (BIGINT)colp->ColBlk); + + if (trace) + htrc("WB: offset=%lld Nrec=%d Deplac=%d Lrecl=%d ColBlk=%d\n", + pos, Nrec, colp->Deplac, Lrecl, colp->ColBlk); + + if (BigSeek(g, Tfile, pos)) + return true; + +//len = colp->Clen * Nrec; see comment in VCTFAM + len = colp->Clen * ((Tdbp->GetMode() == MODE_INSERT) ? CurNum : Nrec); + + if (BigWrite(g, Tfile, colp->Blk->GetValPointer(), len)) + return true; + + return false; + } // end of WriteBlock + +/* ----------------------- End of FilAMVct --------------------------- */ diff --git a/storage/connect/filamvct.h b/storage/connect/filamvct.h new file mode 100644 index 00000000000..22665c6cd23 --- /dev/null +++ b/storage/connect/filamvct.h @@ -0,0 +1,249 @@ +/************** FilAMVct H Declares Source Code File (.H) **************/ +/* Name: FILAMVCT.H Version 1.5 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the VCT file access method classes declares. */ +/***********************************************************************/ +#ifndef __FILAMVCT__ +#define __FILAMVCT__ + +#include "filamfix.h" + +typedef class VCTFAM *PVCTFAM; +typedef class VCTCOL *PVCTCOL; +typedef class VCMFAM *PVCMFAM; +typedef class VECFAM *PVECFAM; +typedef class VMPFAM *PVMPFAM; +typedef class BGVFAM *PBGVFAM; + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in vector format. If MaxBlk=0, each block containing "Elements" */ +/* records, values of each columns are consecutively stored (vector). */ +/* Otherwise, data is arranged by column in the file and MaxBlk is */ +/* used to set the maximum number of blocks. This leave some white */ +/* space allowing to insert new values up to this maximum size. */ +/***********************************************************************/ +class DllExport VCTFAM : public FIXFAM { + friend class TDBVCT; + friend class VCTCOL; + public: + // Constructor + VCTFAM(PVCTDEF tdp); + VCTFAM(PVCTFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_VCT;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) VCTFAM(this);} + + // Methods + virtual void Reset(void); + virtual int MaxBlkSize(PGLOBAL g, int s); + virtual bool AllocateBuffer(PGLOBAL g); + virtual bool InitInsert(PGLOBAL g); + virtual void ResetBuffer(PGLOBAL g) {} + virtual int Cardinality(PGLOBAL g); + virtual int GetRowID(void); + + // Database routines + virtual bool OpenTableFile(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + // Specific functions + virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp); + virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp); + + protected: + virtual bool MakeEmptyFile(PGLOBAL g, char *fn); + virtual bool OpenTempFile(PGLOBAL g); + virtual bool MoveLines(PGLOBAL g) {return false;} + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL); + virtual bool CleanUnusedSpace(PGLOBAL g); + virtual int GetBlockInfo(PGLOBAL g); + virtual bool SetBlockInfo(PGLOBAL g); + bool ResetTableSize(PGLOBAL g, int block, int last); + + // Members + char *NewBlock; // To block written on Insert + char *Colfn; // Pattern for column file names (VER) + char *Tempat; // Pattern for temp file names (VER) + int *Clens; // Pointer to col size array + int *Deplac; // Pointer to col start position array + bool *Isnum; // Pointer to buffer type isnum result + bool AddBlock; // True when adding new blocks on Insert + bool Split; // true: split column file vector format + int Header; // 0: no, 1: separate, 2: in data file + int MaxBlk; // Max number of blocks (True vector format) + int Bsize; // Because Nrec can be modified + int Ncol; // The number of columns; + }; // end of class VCTFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in vector format accessed using file mapping. */ +/***********************************************************************/ +class DllExport VCMFAM : public VCTFAM { + friend class TDBVCT; + friend class VCTCOL; + public: + // Constructor + VCMFAM(PVCTDEF tdp); + VCMFAM(PVCMFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_VMP;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) VCMFAM(this);} + + // Methods + virtual bool AllocateBuffer(PGLOBAL g); + virtual bool InitInsert(PGLOBAL g); + + // Database routines + virtual bool OpenTableFile(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + + // Specific functions + virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp); + virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp); + + // Members + char* Memory; // Pointer on file mapping view. + char* *Memcol; // Pointer on column start. + }; // end of class VCMFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in full vertical format. Each column is contained in a separate */ +/* file whose name is the table name followed by the column number. */ +/***********************************************************************/ +class DllExport VECFAM : public VCTFAM { + friend class TDBVCT; + friend class VCTCOL; + public: + // Constructor + VECFAM(PVCTDEF tdp); + VECFAM(PVECFAM txfp); + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) VECFAM(this);} + + // Methods + virtual bool AllocateBuffer(PGLOBAL g); + virtual bool InitInsert(PGLOBAL g); + virtual void ResetBuffer(PGLOBAL g); + + // Database routines + virtual bool OpenTableFile(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + + // Specific functions + virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp); + virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp); + + protected: + virtual bool OpenTempFile(PGLOBAL g); + virtual bool MoveLines(PGLOBAL g); + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL); + virtual int RenameTempFile(PGLOBAL g); + bool OpenColumnFile(PGLOBAL g, char *opmode, int i); + + // Members + FILE* *Streams; // Points to Dos file structure array + FILE* *T_Streams; // Points to temp file structure array + PFBLOCK *To_Fbs; // Pointer to file block array + PFBLOCK *T_Fbs; // Pointer to temp file block array + void* *To_Bufs; // Pointer to col val block array + bool InitUpdate; // Used to initialize updating + }; // end of class VECFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in full vertical format accessed using file mapping. */ +/***********************************************************************/ +class DllExport VMPFAM : public VCMFAM { + friend class TDBVCT; + friend class VCTCOL; + public: + // Constructor + VMPFAM(PVCTDEF tdp); + VMPFAM(PVMPFAM txfp); + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) VMPFAM(this);} + + // Methods + virtual bool AllocateBuffer(PGLOBAL g); + + // Database routines + virtual bool OpenTableFile(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + + protected: + bool MapColumnFile(PGLOBAL g, MODE mode, int i); + + // Members + PFBLOCK *To_Fbs; // Pointer to file block array + }; // end of class VMPFAM + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in (possibly blocked) vector format that can be larger than 2GB. */ +/***********************************************************************/ +class BGVFAM : public VCTFAM { + friend class VCTCOL; + public: + // Constructors + BGVFAM(PVCTDEF tdp); + BGVFAM(PBGVFAM txfp); + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) BGVFAM(this);} + + // Methods + virtual bool AllocateBuffer(PGLOBAL g); + + // Database routines + virtual bool OpenTableFile(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + // Specific functions + virtual bool ReadBlock(PGLOBAL g, PVCTCOL colp); + virtual bool WriteBlock(PGLOBAL g, PVCTCOL colp); + + protected: + bool BigSeek(PGLOBAL g, HANDLE h, BIGINT pos, bool b = false); + bool BigRead(PGLOBAL g, HANDLE h, void *inbuf, int req); + bool BigWrite(PGLOBAL g, HANDLE h, void *inbuf, int req); + virtual bool MakeEmptyFile(PGLOBAL g, char *fn); + virtual bool OpenTempFile(PGLOBAL g); + virtual bool MoveIntermediateLines(PGLOBAL g, bool *b = NULL); + virtual bool CleanUnusedSpace(PGLOBAL g); + virtual bool SetBlockInfo(PGLOBAL g); + virtual int GetBlockInfo(PGLOBAL g); + + // Members + HANDLE Hfile; // Handle to big file + HANDLE Tfile; // Handle to temporary file + BIGINT *BigDep; // Pointer to col start position array + }; // end of class BGVFAM + +#endif // __FILAMVCT__ + diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp new file mode 100644 index 00000000000..4f1a02d8257 --- /dev/null +++ b/storage/connect/filamzip.cpp @@ -0,0 +1,821 @@ +/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMZIP */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the ZLIB compressed files classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#else // !UNIX +#include <io.h> +#endif +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +//#include "catalog.h" +//#include "reldef.h" +//#include "xobject.h" +//#include "kindex.h" +#include "filamtxt.h" +#include "tabdos.h" +#if defined(UNIX) +#include "osutil.h" +#endif + +/***********************************************************************/ +/* This define prepares ZLIB function declarations. */ +/***********************************************************************/ +//#define ZLIB_DLL + +#include "filamzip.h" + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +extern int num_read, num_there, num_eq[]; // Statistics + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the ZIPFAM class. */ +/***********************************************************************/ +ZIPFAM::ZIPFAM(PZIPFAM txfp) : TXTFAM(txfp) + { + Zfile = txfp->Zfile; + Zpos = txfp->Zpos; + } // end of ZIPFAM copy constructor + +/***********************************************************************/ +/* Zerror: Error function for gz calls. */ +/* gzerror returns the error message for the last error which occurred*/ +/* on the given compressed file. errnum is set to zlib error number. */ +/* If an error occurred in the file system and not in the compression */ +/* library, errnum is set to Z_ERRNO and the application may consult */ +/* errno to get the exact error code. */ +/***********************************************************************/ +int ZIPFAM::Zerror(PGLOBAL g) + { + int errnum; + + strcpy(g->Message, gzerror(Zfile, &errnum)); + + if (errnum == Z_ERRNO) +#if defined(WIN32) + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(NULL)); +#else // !WIN32 + sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno)); +#endif // !WIN32 + + return (errnum == Z_STREAM_END) ? RC_EF : RC_FX; + } // end of Zerror + +/***********************************************************************/ +/* Reset: reset position values at the beginning of file. */ +/***********************************************************************/ +void ZIPFAM::Reset(void) + { + TXTFAM::Reset(); +//gzrewind(Zfile); // Useful ????? + Zpos = 0; + } // end of Reset + +/***********************************************************************/ +/* ZIP GetFileLength: returns an estimate of what would be the */ +/* uncompressed file size in number of bytes. */ +/***********************************************************************/ +int ZIPFAM::GetFileLength(PGLOBAL g) + { + int len = TXTFAM::GetFileLength(g); + + if (len > 0) + // Estimate size reduction to a max of 6 + len *= 6; + + return len; + } // end of GetFileLength + +/***********************************************************************/ +/* ZIP Access Method opening routine. */ +/***********************************************************************/ +bool ZIPFAM::OpenTableFile(PGLOBAL g) + { + char opmode[4], filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + + switch (mode) { + case MODE_READ: + strcpy(opmode, "r"); + break; + case MODE_UPDATE: + /*****************************************************************/ + /* Updating ZIP files not implemented yet. */ + /*****************************************************************/ + strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP)); + return true; + case MODE_DELETE: + if (!Tdbp->GetNext()) { + // Store the number of deleted lines + DelRows = Cardinality(g); + + // This will erase the entire file + strcpy(opmode, "w"); +// Block = 0; // For ZBKFAM +// Last = Nrec; // For ZBKFAM + Tdbp->ResetSize(); + } else { + sprintf(g->Message, MSG(NO_PART_DEL), "ZIP"); + return true; + } // endif filter + + break; + case MODE_INSERT: + strcpy(opmode, "a+"); + break; + default: + sprintf(g->Message, MSG(BAD_OPEN_MODE), mode); + return true; + } // endswitch Mode + + /*********************************************************************/ + /* Open according to logical input/output mode required. */ + /* Use specific zlib functions. */ + /* Treat files as binary. */ + /*********************************************************************/ + strcat(opmode, "b"); + Zfile = gzopen(PlugSetPath(filename, To_File, Tdbp->GetPath()), opmode); + + if (Zfile == NULL) { + sprintf(g->Message, MSG(GZOPEN_ERROR), + opmode, (int)errno, filename); + strcat(strcat(g->Message, ": "), strerror(errno)); + return (mode == MODE_READ && errno == ENOENT) + ? PushWarning(g, Tdbp) : true; + } // endif Zfile + + /*********************************************************************/ + /* Something to be done here. >>>>>>>> NOT DONE <<<<<<<< */ + /*********************************************************************/ +//To_Fb = dbuserp->Openlist; // Keep track of File block + + /*********************************************************************/ + /* Allocate the line buffer. */ + /*********************************************************************/ + return AllocateBuffer(g); + } // end of OpenTableFile + +/***********************************************************************/ +/* Allocate the line buffer. For mode Delete a bigger buffer has to */ +/* be allocated because is it also used to move lines into the file. */ +/***********************************************************************/ +bool ZIPFAM::AllocateBuffer(PGLOBAL g) + { + MODE mode = Tdbp->GetMode(); + + Buflen = Lrecl + 2; // Lrecl does not include CRLF +//Buflen *= ((Mode == MODE_DELETE) ? DOS_BUFF_LEN : 1); NIY + +#ifdef DEBTRACE + htrc("SubAllocating a buffer of %d bytes\n", Buflen); +#endif + + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* For Insert buffer must be prepared. */ + /*******************************************************************/ + memset(To_Buf, ' ', Buflen); + To_Buf[Buflen - 2] = '\n'; + To_Buf[Buflen - 1] = '\0'; + } // endif Insert + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int ZIPFAM::GetRowID(void) + { + return Rows; + } // end of GetRowID + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int ZIPFAM::GetPos(void) + { + return (int)Zpos; + } // end of GetPos + +/***********************************************************************/ +/* GetNextPos: return the position of next record. */ +/***********************************************************************/ +int ZIPFAM::GetNextPos(void) + { + return gztell(Zfile); + } // end of GetNextPos + +/***********************************************************************/ +/* SetPos: Replace the table at the specified position. */ +/***********************************************************************/ +bool ZIPFAM::SetPos(PGLOBAL g, int pos) + { + sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP"); + return true; +#if 0 + Fpos = pos; + + if (fseek(Stream, Fpos, SEEK_SET)) { + sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos); + return true; + } // endif + + Placed = true; + return false; +#endif // 0 + } // end of SetPos + +/***********************************************************************/ +/* Record file position in case of UPDATE or DELETE. */ +/***********************************************************************/ +bool ZIPFAM::RecordPos(PGLOBAL g) + { + Zpos = gztell(Zfile); + return false; + } // end of RecordPos + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int ZIPFAM::SkipRecord(PGLOBAL g, bool header) + { + // Skip this record + if (gzeof(Zfile)) + return RC_EF; + else if (gzgets(Zfile, To_Buf, Buflen) == Z_NULL) + return Zerror(g); + + if (header) + RecordPos(g); + + return RC_OK; + } // end of SkipRecord + +/***********************************************************************/ +/* ReadBuffer: Read one line from a compressed text file. */ +/***********************************************************************/ +int ZIPFAM::ReadBuffer(PGLOBAL g) + { + char *p; + int rc; + + if (!Zfile) + return RC_EF; + + if (!Placed) { + /*******************************************************************/ + /* Record file position in case of UPDATE or DELETE. */ + /*******************************************************************/ + if (RecordPos(g)) + return RC_FX; + + CurBlk = Rows++; // Update RowID + } else + Placed = false; + + if (gzeof(Zfile)) { + rc = RC_EF; + } else if (gzgets(Zfile, To_Buf, Buflen) != Z_NULL) { + p = To_Buf + strlen(To_Buf) - 1; + + if (*p == '\n') + *p = '\0'; // Eliminate ending new-line character + + if (*(--p) == '\r') + *p = '\0'; // Eliminate eventuel carriage return + + strcpy(Tdbp->GetLine(), To_Buf); + IsRead = true; + rc = RC_OK; + num_read++; + } else + rc = Zerror(g); + +#ifdef DEBTRACE + htrc(" Read: '%s' rc=%d\n", To_Buf, rc); +#endif + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteDB: Data Base write routine for ZDOS access method. */ +/* Update is not possible without using a temporary file (NIY). */ +/***********************************************************************/ +int ZIPFAM::WriteBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* Prepare the write buffer. */ + /*********************************************************************/ + strcat(strcpy(To_Buf, Tdbp->GetLine()), CrLf); + + /*********************************************************************/ + /* Now start the writing process. */ + /*********************************************************************/ + if (gzputs(Zfile, To_Buf) < 0) + return Zerror(g); + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for ZDOS access method. (NIY) */ +/***********************************************************************/ +int ZIPFAM::DeleteRecords(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(NO_ZIP_DELETE)); + return RC_FX; + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for DOS access method. */ +/***********************************************************************/ +void ZIPFAM::CloseTableFile(PGLOBAL g) + { + int rc = gzclose(Zfile); + +#ifdef DEBTRACE + htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc); +#endif + + Zfile = NULL; // So we can know whether table is open +//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for ZIP access method. */ +/***********************************************************************/ +void ZIPFAM::Rewind(void) + { + gzrewind(Zfile); + } // end of Rewind + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp) + { + Blocked = true; + Block = tdp->GetBlock(); + Last = tdp->GetLast(); + Nrec = tdp->GetElemt(); + CurLine = NULL; + NxtLine = NULL; + Closing = false; + BlkPos = tdp->GetTo_Pos(); + } // end of ZBKFAM standard constructor + +ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp) + { + CurLine = txfp->CurLine; + NxtLine = txfp->NxtLine; + Closing = txfp->Closing; + } // end of ZBKFAM copy constructor + +/***********************************************************************/ +/* Use BlockTest to reduce the table estimated size. */ +/***********************************************************************/ +int ZBKFAM::MaxBlkSize(PGLOBAL g, int s) + { + int savcur = CurBlk; + int size; + + // Roughly estimate the table size as the sum of blocks + // that can contain good rows + for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++) + size += (CurBlk == Block - 1) ? Last : Nrec; + + CurBlk = savcur; + return size; + } // end of MaxBlkSize + +/***********************************************************************/ +/* ZBK Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int ZBKFAM::Cardinality(PGLOBAL g) + { + // Should not be called in this version + return (g) ? -1 : 0; +//return (g) ? (int)((Block - 1) * Nrec + Last) : 1; + } // end of Cardinality + +/***********************************************************************/ +/* Allocate the line buffer. For mode Delete a bigger buffer has to */ +/* be allocated because is it also used to move lines into the file. */ +/***********************************************************************/ +bool ZBKFAM::AllocateBuffer(PGLOBAL g) + { + Buflen = Nrec * (Lrecl + 2); + CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (Tdbp->GetMode() == MODE_INSERT) { + // Set values so Block and Last can be recalculated + if (Last == Nrec) { + CurBlk = Block; + Rbuf = Nrec; // To be used by WriteDB + } else { + // The last block must be completed + CurBlk = Block - 1; + Rbuf = Nrec - Last; // To be used by WriteDB + } // endif Last + + } // endif Insert + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* GetRowID: return the RowID of last read record. */ +/***********************************************************************/ +int ZBKFAM::GetRowID(void) + { + return CurNum + Nrec * CurBlk + 1; + } // end of GetRowID + +/***********************************************************************/ +/* GetPos: return the position of last read record. */ +/***********************************************************************/ +int ZBKFAM::GetPos(void) + { + return CurNum + Nrec * CurBlk; // Computed file index + } // end of GetPos + +/***********************************************************************/ +/* Record file position in case of UPDATE or DELETE. */ +/* Not used yet for fixed tables. */ +/***********************************************************************/ +bool ZBKFAM::RecordPos(PGLOBAL g) + { +//strcpy(g->Message, "RecordPos not implemented for zip blocked tables"); +//return true; + return RC_OK; + } // end of RecordPos + +/***********************************************************************/ +/* Skip one record in file. */ +/***********************************************************************/ +int ZBKFAM::SkipRecord(PGLOBAL g, bool header) + { +//strcpy(g->Message, "SkipRecord not implemented for zip blocked tables"); +//return RC_FX; + return RC_OK; + } // end of SkipRecord + +/***********************************************************************/ +/* ReadBuffer: Read one line from a compressed text file. */ +/***********************************************************************/ +int ZBKFAM::ReadBuffer(PGLOBAL g) + { + int n, rc = RC_OK; + + /*********************************************************************/ + /* Sequential reading when Placed is not true. */ + /*********************************************************************/ + if (++CurNum < Rbuf) { + CurLine = NxtLine; + + // Get the position of the next line in the buffer + while (*NxtLine++ != '\n') ; + + // Set caller line buffer + n = NxtLine - CurLine - Ending; + memcpy(Tdbp->GetLine(), CurLine, n); + Tdbp->GetLine()[n] = '\0'; + return RC_OK; + } else if (Rbuf < Nrec && CurBlk != -1) + return RC_EF; + + /*********************************************************************/ + /* New block. */ + /*********************************************************************/ + CurNum = 0; + + if (++CurBlk >= Block) + return RC_EF; + + BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk]; + + if (!(n = gzread(Zfile, To_Buf, BlkLen))) { + rc = RC_EF; + } else if (n > 0) { + // Get the position of the current line + CurLine = To_Buf; + + // Now get the position of the next line + for (NxtLine = CurLine; *NxtLine++ != '\n';) ; + + // Set caller line buffer + n = NxtLine - CurLine - Ending; + memcpy(Tdbp->GetLine(), CurLine, n); + Tdbp->GetLine()[n] = '\0'; + Rbuf = (CurBlk == Block - 1) ? Last : Nrec; + IsRead = true; + rc = RC_OK; + num_read++; + } else + rc = Zerror(g); + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteDB: Data Base write routine for ZDOS access method. */ +/* Update is not possible without using a temporary file (NIY). */ +/***********************************************************************/ +int ZBKFAM::WriteBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* Prepare the write buffer. */ + /*********************************************************************/ + if (!Closing) + strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf); + + /*********************************************************************/ + /* In Insert mode, blocs are added sequentialy to the file end. */ + /* Note: Update mode is not handled for zip files. */ + /*********************************************************************/ + if (++CurNum == Rbuf) { + /*******************************************************************/ + /* New block, start the writing process. */ + /*******************************************************************/ + BlkLen = CurLine + strlen(CurLine) - To_Buf; + + if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen || + gzflush(Zfile, Z_FULL_FLUSH)) { + Closing = true; + return Zerror(g); + } // endif gzwrite + + Rbuf = Nrec; + CurBlk++; + CurNum = 0; + CurLine = To_Buf; + } else + CurLine += strlen(CurLine); + + return RC_OK; + } // end of WriteBuffer + +/***********************************************************************/ +/* Data Base delete line routine for ZBK access method. */ +/* Implemented only for total deletion of the table, which is done */ +/* by opening the file in mode "wb". */ +/***********************************************************************/ +int ZBKFAM::DeleteRecords(PGLOBAL g, int irc) + { + if (irc == RC_EF) { + LPCSTR name = Tdbp->GetName(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + PCATLG cat = PlgGetCatalog(g); + + defp->SetBlock(0); + defp->SetLast(Nrec); + + if (!cat->SetIntCatInfo("Blocks", 0) || + !cat->SetIntCatInfo("Last", 0)) { + sprintf(g->Message, MSG(UPDATE_ERROR), "Header"); + return RC_FX; + } else + return RC_OK; + + } else + return irc; + + } // end of DeleteRecords + +/***********************************************************************/ +/* Data Base close routine for ZBK access method. */ +/***********************************************************************/ +void ZBKFAM::CloseTableFile(PGLOBAL g) + { + int rc = RC_OK; + + if (Tdbp->GetMode() == MODE_INSERT) { + PCATLG cat = PlgGetCatalog(g); + LPCSTR name = Tdbp->GetName(); + PDOSDEF defp = (PDOSDEF)Tdbp->GetDef(); + + if (CurNum && !Closing) { + // Some more inserted lines remain to be written + Last = (Nrec - Rbuf) + CurNum; + Block = CurBlk + 1; + Rbuf = CurNum--; + Closing = true; + rc = WriteBuffer(g); + } else if (Rbuf == Nrec) { + Last = Nrec; + Block = CurBlk; + } // endif CurNum + + if (rc != RC_FX) { + defp->SetBlock(Block); + defp->SetLast(Last); + cat->SetIntCatInfo("Blocks", Block); + cat->SetIntCatInfo("Last", Last); + } // endif + + gzclose(Zfile); + } else if (Tdbp->GetMode() == MODE_DELETE) { + rc = DeleteRecords(g, RC_EF); + gzclose(Zfile); + } else + rc = gzclose(Zfile); + +#ifdef DEBTRACE + htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc); +#endif + + Zfile = NULL; // So we can know whether table is open +//To_Fb->Count = 0; // Avoid double closing by PlugCloseAll + } // end of CloseTableFile + +/***********************************************************************/ +/* Rewind routine for ZBK access method. */ +/***********************************************************************/ +void ZBKFAM::Rewind(void) + { + gzrewind(Zfile); + CurBlk = -1; + CurNum = Rbuf; + } // end of Rewind + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +ZIXFAM::ZIXFAM(PDOSDEF tdp) : ZBKFAM(tdp) + { +//Block = tdp->GetBlock(); +//Last = tdp->GetLast(); + Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; + Blksize = Nrec * Lrecl; + } // end of ZIXFAM standard constructor + +/***********************************************************************/ +/* ZIX Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int ZIXFAM::Cardinality(PGLOBAL g) + { + if (Last) + return (g) ? (int)((Block - 1) * Nrec + Last) : 1; + else // Last and Block not defined, cannot do it yet + return 0; + + } // end of Cardinality + +/***********************************************************************/ +/* Allocate the line buffer. For mode Delete a bigger buffer has to */ +/* be allocated because is it also used to move lines into the file. */ +/***********************************************************************/ +bool ZIXFAM::AllocateBuffer(PGLOBAL g) + { + Buflen = Blksize; + To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); + + if (Tdbp->GetMode() == MODE_INSERT) { + /*******************************************************************/ + /* For Insert the buffer must be prepared. */ + /*******************************************************************/ + memset(To_Buf, ' ', Buflen); + + if (Tdbp->GetFtype() < 2) + // if not binary, the file is physically a text file + for (int len = Lrecl; len <= Buflen; len += Lrecl) { +#if defined(WIN32) + To_Buf[len - 2] = '\r'; +#endif // WIN32 + To_Buf[len - 1] = '\n'; + } // endfor len + + // Set values so Block and Last can be recalculated + if (Last == Nrec) { + CurBlk = Block; + Rbuf = Nrec; // To be used by WriteDB + } else { + // The last block must be completed + CurBlk = Block - 1; + Rbuf = Nrec - Last; // To be used by WriteDB + } // endif Last + + } // endif Insert + + return false; + } // end of AllocateBuffer + +/***********************************************************************/ +/* ReadBuffer: Read one line from a compressed text file. */ +/***********************************************************************/ +int ZIXFAM::ReadBuffer(PGLOBAL g) + { + int n, rc = RC_OK; + + /*********************************************************************/ + /* Sequential reading when Placed is not true. */ + /*********************************************************************/ + if (++CurNum < Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; + } else if (Rbuf < Nrec && CurBlk != -1) + return RC_EF; + + /*********************************************************************/ + /* New block. */ + /*********************************************************************/ + CurNum = 0; + Tdbp->SetLine(To_Buf); + +//if (++CurBlk >= Block) +// return RC_EF; + + if (!(n = gzread(Zfile, To_Buf, Buflen))) { + rc = RC_EF; + } else if (n > 0) { + Rbuf = n / Lrecl; + IsRead = true; + rc = RC_OK; + num_read++; + } else + rc = Zerror(g); + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* WriteDB: Data Base write routine for ZDOS access method. */ +/* Update is not possible without using a temporary file (NIY). */ +/***********************************************************************/ +int ZIXFAM::WriteBuffer(PGLOBAL g) + { + /*********************************************************************/ + /* In Insert mode, blocs are added sequentialy to the file end. */ + /* Note: Update mode is not handled for zip files. */ + /*********************************************************************/ + if (++CurNum == Rbuf) { + /*******************************************************************/ + /* New block, start the writing process. */ + /*******************************************************************/ + BlkLen = Rbuf * Lrecl; + + if (gzwrite(Zfile, To_Buf, BlkLen) != BlkLen || + gzflush(Zfile, Z_FULL_FLUSH)) { + Closing = true; + return Zerror(g); + } // endif gzwrite + + Rbuf = Nrec; + CurBlk++; + CurNum = 0; + Tdbp->SetLine(To_Buf); + } else + Tdbp->IncLine(Lrecl); // Used by FIXCOL functions + + return RC_OK; + } // end of WriteBuffer + +/* ------------------------ End of ZipFam ---------------------------- */ diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h new file mode 100644 index 00000000000..a83af78b19a --- /dev/null +++ b/storage/connect/filamzip.h @@ -0,0 +1,174 @@ +/************** FilAmZip H Declares Source Code File (.H) **************/ +/* Name: FILAMZIP.H Version 1.1 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the GZIP access method classes declares. */ +/***********************************************************************/ +#ifndef __FILAMZIP_H +#define __FILAMZIP_H + +#include "zlib.h" + +#define TYPE_AM_ZIP (AMT)150 +#define TYPE_AM_ZLIB (AMT)155 + +typedef class ZIPFAM *PZIPFAM; +typedef class ZBKFAM *PZBKFAM; +typedef class ZIXFAM *PZIXFAM; +typedef class ZLBFAM *PZLBFAM; + +/***********************************************************************/ +/* This is the access method class declaration for not optimized */ +/* variable record length files compressed using the gzip library */ +/* functions. File is accessed record by record (row). */ +/***********************************************************************/ +class DllExport ZIPFAM : public TXTFAM { +// friend class DOSCOL; + public: + // Constructor + ZIPFAM(PDOSDEF tdp) : TXTFAM(tdp) {Zfile = NULL; Zpos = 0;} + ZIPFAM(PZIPFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + virtual int GetPos(void); + virtual int GetNextPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) ZIPFAM(this);} + + // Methods + virtual void Reset(void); + virtual int GetFileLength(PGLOBAL g); + virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;} + virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} + virtual bool AllocateBuffer(PGLOBAL g); + virtual int GetRowID(void); + virtual bool RecordPos(PGLOBAL g); + virtual bool SetPos(PGLOBAL g, int recpos); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual bool OpenTableFile(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + int Zerror(PGLOBAL g); // GZ error function + + // Members + gzFile Zfile; // Points to GZ file structure + z_off_t Zpos; // Uncompressed file position + }; // end of class ZIPFAM + +/***********************************************************************/ +/* This is the access method class declaration for optimized variable */ +/* record length files compressed using the gzip library functions. */ +/* The File is accessed by block (requires an opt file). */ +/***********************************************************************/ +class DllExport ZBKFAM : public ZIPFAM { + public: + // Constructor + ZBKFAM(PDOSDEF tdp); + ZBKFAM(PZBKFAM txfp); + + // Implementation + virtual int GetPos(void); + virtual int GetNextPos(void) {return 0;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) ZBKFAM(this);} + + // Methods + virtual int Cardinality(PGLOBAL g); + virtual int MaxBlkSize(PGLOBAL g, int s); + virtual bool AllocateBuffer(PGLOBAL g); + virtual int GetRowID(void); + virtual bool RecordPos(PGLOBAL g); + virtual int SkipRecord(PGLOBAL g, bool header); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + // Members + char *CurLine; // Position of current line in buffer + char *NxtLine; // Position of Next line in buffer + bool Closing; // True when closing on Insert + }; // end of class ZBKFAM + +/***********************************************************************/ +/* This is the access method class declaration for fixed record */ +/* length files compressed using the gzip library functions. */ +/* The file is always accessed by block. */ +/***********************************************************************/ +class DllExport ZIXFAM : public ZBKFAM { + public: + // Constructor + ZIXFAM(PDOSDEF tdp); + ZIXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {} + + // Implementation + virtual int GetNextPos(void) {return 0;} + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) ZIXFAM(this);} + + // Methods + virtual int Cardinality(PGLOBAL g); + virtual bool AllocateBuffer(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + + protected: + // No additional Members + }; // end of class ZIXFAM + +#ifdef NOT_USED +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for PlugDB */ +/* fixed/variable files compressed using the zlib library functions. */ +/* Physically these are written and read using the same technique */ +/* than blocked variable files, only the contain of each block is */ +/* compressed using the deflate zlib function. The purpose of this */ +/* specific format is to have a fast mechanism for direct access of */ +/* records so blocked optimization is fast and direct access (joins) */ +/* is allowed. Note that the block length is written ahead of each */ +/* block to enable reading when optimization file is not available. */ +/***********************************************************************/ +class DllExport ZLBFAM : public BLKFAM { + public: + // Constructor + ZLBFAM(PDOSDEF tdp); + ZLBFAM(PZLBFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZLIB;} + virtual int GetPos(void); + virtual int GetNextPos(void); + virtual PTXF Duplicate(PGLOBAL g) + {return (PTXF)new(g) ZLBFAM(this);} + inline void SetOptimized(bool b) {Optimized = b;} + + // Methods + virtual int GetFileLength(PGLOBAL g); + virtual bool AllocateBuffer(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual void CloseTableFile(PGLOBAL g); + virtual void Rewind(void); + + protected: + bool WriteCompressedBuffer(PGLOBAL g); + int ReadCompressedBuffer(PGLOBAL g, void *rdbuf); + + // Members + z_streamp Zstream; // Compression/decompression stream + Byte *Zbuffer; // Compressed block buffer + int *Zlenp; // Pointer to block length + bool Optimized; // true when opt file is available + }; // end of class ZLBFAM +#endif // NOT_USED + +#endif // __FILAMZIP_H diff --git a/storage/connect/fmdlex.c b/storage/connect/fmdlex.c new file mode 100644 index 00000000000..9ab8f2e03c5 --- /dev/null +++ b/storage/connect/fmdlex.c @@ -0,0 +1,1525 @@ +#define yyFlexLexer fmdfFlexLexer +#define yy_create_buffer fmdf_create_buffer +#define yy_delete_buffer fmdf_delete_buffer +#define yy_flex_debug fmdf_flex_debug +#define yy_init_buffer fmdf_init_buffer +#define yy_load_buffer_state fmdf_load_buffer_state +#define yy_switch_to_buffer fmdf_switch_to_buffer +#define yyin fmdfin +#define yyleng fmdfleng +#define yylex fmdflex +#define yyout fmdfout +#define yyrestart fmdfrestart +#define yytext fmdftext +#define yywrap fmdfwrap + +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/flex-2.4.7/RCS/flex.skl,v 1.2 94/08/03 11:13:24 vern Exp $ + */ + +#define FLEX_SCANNER + +#if WIN32 +#define __STDC__ 1 +#endif + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> +#include <unistd.h> + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#ifdef __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + + +#ifdef __TURBOC__ +#define YY_USE_CONST +#endif + + +#ifndef YY_USE_CONST +#ifndef const +#define const +#endif +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. + */ +#define YY_START ((yy_start - 1) / 2) + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". Now included + * only for backward compatibility with previous versions of flex. + */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#ifdef __cplusplus +extern "C" { +#endif + extern int yywrap YY_PROTO(( void )); +#ifdef __cplusplus + } +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +void yyrestart YY_PROTO(( FILE *input_file )); +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); + +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +static void yy_push_state YY_PROTO(( int new_state )); +static void yy_pop_state YY_PROTO(( void )); +static int yy_top_state YY_PROTO(( void )); + +static void *yy_flex_alloc YY_PROTO(( unsigned int )); +static void *yy_flex_realloc YY_PROTO(( void *, unsigned int )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define INITIAL 0 +#define txt 1 +#define sqt 2 +#define dqt 3 +typedef unsigned char YY_CHAR; +typedef int yy_state_type; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +extern char *yytext; +#define yytext_ptr yytext + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, const char *, int )); +#endif + +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( const char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = yy_cp - yy_bp; \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_END_OF_BUFFER 17 +static const short int yy_accept[45] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 15, + 16, 14, 1, 13, 2, 3, 8, 7, 6, 9, + 10, 11, 12, 15, 16, 5, 15, 16, 4, 1, + 8, 8, 7, 7, 6, 9, 10, 11, 12, 0, + 5, 0, 4, 0 + } ; + +static const int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 5, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 9, 1, 1, 1, 1, 10, 1, + 1, 1, 1, 1, 11, 12, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const int yy_meta[13] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1 + } ; + +static const short int yy_base[52] = + { 0, + 49, 48, 0, 0, 10, 13, 16, 18, 50, 53, + 53, 53, 47, 53, 53, 53, 42, 40, 38, 36, + 34, 32, 30, 36, 35, 53, 35, 34, 53, 35, + 53, 30, 53, 28, 26, 24, 22, 20, 18, 24, + 53, 24, 53, 53, 26, 25, 24, 23, 22, 16, + 13 + } ; + +static const short int yy_def[52] = + { 0, + 45, 45, 44, 3, 46, 46, 47, 47, 44, 44, + 44, 44, 44, 44, 44, 44, 48, 49, 44, 44, + 44, 44, 44, 50, 50, 44, 51, 51, 44, 44, + 44, 48, 44, 49, 44, 44, 44, 44, 44, 50, + 44, 51, 44, 0, 44, 44, 44, 44, 44, 44, + 44 + } ; + +static const short int yy_nxt[66] = + { 0, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 25, 42, 26, 25, 40, 26, 28, 29, + 28, 29, 33, 31, 27, 24, 10, 43, 41, 39, + 38, 37, 36, 35, 34, 32, 30, 43, 43, 41, + 41, 39, 38, 37, 36, 35, 34, 32, 30, 44, + 11, 11, 9, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44 + } ; + +static const short int yy_chk[66] = + { 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 5, 51, 5, 6, 50, 6, 7, 7, + 8, 8, 49, 48, 47, 46, 45, 42, 40, 39, + 38, 37, 36, 35, 34, 32, 30, 28, 27, 25, + 24, 23, 22, 21, 20, 19, 18, 17, 13, 9, + 2, 1, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +char *yytext; +#if defined(UNIX) +#define _fileno fileno +#define istty _istty +#else +#include <stdlib.h> +#include <io.h> +#endif /* UNIX */ +#include <string.h> +#include "preparse.h" + +#define isatty _isatty +#define fileno _fileno +#undef YY_DECL +#define YY_DECL int yylex YY_PROTO((PDTP ppp)) +#define YYSTATE ((yy_start-1)/2) +#undef YY_INPUT +#define YY_INPUT(buf,n,m) \ + {n=(m<strlen(pp->Curp))?m:strlen(pp->Curp);strncpy(buf,pp->Curp,n);pp->Curp+=n;} +#if defined(UNIX) +#undef yywrap +#define yywrap ddwrap +#endif /* UNIX */ +static PDTP pp; +static void MakeParm(int n); +static void MakeMMDD(int n); +static void MakeAMPM(int n); +static void MakeIn(char *); +static void MakeOut(char *); +static void Quotin(char *); +static void Quotout(char *); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = getc( yyin ); \ + result = c == EOF ? 0 : 1; \ + buf[0] = (char) c; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + + + /*************************************************************************/ + /* Flex parser to analyze date format and produce input and/or output */ + /* formats. Non quoted blanks are significant (put in the output format) */ + /* unless the Flag is not null, then there are ignored for output fmt. */ + /*************************************************************************/ + BEGIN txt; + pp = ppp; + pp->Num = 0; + if (pp->InFmt) {*pp->InFmt = '\0'; pp->InFmt[pp->Outsize -1] = '\0'; } + if (pp->OutFmt) {*pp->OutFmt = '\0'; pp->OutFmt[pp->Outsize -1] = '\0'; } + pp->Curp = pp->Format; + if (!yy_init) { /* Restart that stupid Flex otherwise parsing last input */ + yy_init_buffer( yy_current_buffer, yyin ); + yy_load_buffer_state(); + } // endif yy_init + + + if ( yy_init ) + { +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 45 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 53 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_USER_ACTION +{MakeIn(" "); if (!pp->Flag) MakeOut(" ");} + YY_BREAK +case 2: +YY_USER_ACTION +{BEGIN dqt;} + YY_BREAK +case 3: +YY_USER_ACTION +{BEGIN sqt;} + YY_BREAK +case 4: +YY_USER_ACTION +{Quotin(yytext); Quotout(yytext); +#ifdef DEBUG + fprintf(stderr, "In double quote yytext=>>%s<<\n", yytext); +#endif + BEGIN txt;} + YY_BREAK +case 5: +YY_USER_ACTION +{Quotin(yytext); Quotout(yytext); +#ifdef DEBUG + fprintf(stderr, "In simple quote yytext=>>%s<<\n", yytext); +#endif + BEGIN txt;} + YY_BREAK +case 6: +YY_USER_ACTION +MakeParm(0); + YY_BREAK +case 7: +YY_USER_ACTION +MakeMMDD(1); + YY_BREAK +case 8: +YY_USER_ACTION +MakeMMDD(2); + YY_BREAK +case 9: +YY_USER_ACTION +MakeParm(3); + YY_BREAK +case 10: +YY_USER_ACTION +MakeParm(4); + YY_BREAK +case 11: +YY_USER_ACTION +MakeParm(5); + YY_BREAK +case 12: +YY_USER_ACTION +MakeAMPM(6); + YY_BREAK +case 13: +YY_USER_ACTION + + YY_BREAK +case 14: +YY_USER_ACTION +{MakeIn(yytext); MakeOut(yytext); +#ifdef DEBUG + fprintf(stderr, "No quote char=>>%s<<\n", yytext); +#endif + } + YY_BREAK +case 15: +YY_USER_ACTION +{pp->Flag = -1; yyterminate();} + YY_BREAK +case 16: +YY_USER_ACTION +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(txt): +case YY_STATE_EOF(sqt): +case YY_STATE_EOF(dqt): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = yy_cp - yytext_ptr - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a singled characater, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = yy_c_buf_p - yytext_ptr; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = yy_c_buf_p - b->yy_ch_buf; + + b->yy_buf_size *= 2; + b->yy_ch_buf = (char *) + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move - YY_MORE_ADJ == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active. + */ + + yytext_ptr = &yy_current_buffer->yy_ch_buf[1]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 45 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 45 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 44); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = (char) c; + + + /* Note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly. + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext_ptr = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = + yytext_ptr + YY_MORE_ADJ; + return EOF; + } + + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( + "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( + "unexpected last match in input()" ); +#endif + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + yy_flex_free( (void *) b->yy_ch_buf ); + yy_flex_free( (void *) b ); + } + + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + { + b->yy_input_file = file; + + /* We put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_is_interactive = file ? isatty( fileno(file) ) : 0; + + b->yy_fill_buffer = 1; + + b->yy_buffer_status = YY_BUFFER_NEW; + } + + +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + int new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } + + +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } + + +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } + + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( const char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( 1 ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n - YY_MORE_ADJ; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, const char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +const char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( unsigned int size ) +#else +static void *yy_flex_alloc( size ) +unsigned int size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, unsigned int size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +unsigned int size; +#endif + { + return (void *) realloc( ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + + +void MakeMMDD(int n) + { + int m = strlen(yytext) - 1; + char c = yytext[m]; + + if ((c == 'M' && n == 1) || (c == 'D' && n == 2)) { + m++; + c = 0; + } /* endif c */ + +#ifdef DEBUG + fprintf(stderr, "MM: n=%d num=%d InFmt=%s OutFmt=%s;\n", + n, pp->Num, pp->InFmt, pp->OutFmt); +#endif + pp->Index[pp->Num++] = (m < 3) ? n : (-n); + + switch (m) { + case 1: + MakeIn("%2d"); + MakeOut((n == 1) ? "%#m" : "%#d"); + break; + case 2: + MakeIn("%2d"); + MakeOut((n == 1) ? "%m" : "%d"); + break; + case 3: + MakeIn("%3s"); + MakeOut((n == 1) ? "%b" : "%a"); + break; + default: + if (c && c != ' ') { + char fm[] = "%[^x]"; + fm[3] = c; + MakeIn(fm); + } else + MakeIn("%s"); + + MakeOut((n == 1) ? "%B" : "%A"); + } /* endswitch m */ + + if (c) + unput(c); + + } /* end of MakeMMDD */ + +void MakeParm(int n) + { + int m = strlen(yytext); + +#ifdef DEBUG + fprintf(stderr, "MP: n=%d num=%d InFmt=%s OutFmt=%s;\n", + n, pp->Num, pp->InFmt, pp->OutFmt); +#endif + pp->Index[pp->Num++] = n; + + switch (m) { + case 1: + MakeIn("%2d"); + MakeOut((n == 0) ? "%#y" : (n == 3) ? "%#H" + : (n == 4) ? "%#M" : "%#S"); + break; + case 2: + MakeIn("%2d"); + MakeOut((n == 0) ? "%y" : (n == 3) ? "%H" + : (n == 4) ? "%M" : "%S"); + break; + default: + MakeIn("%4d"); + MakeOut("%Y"); + } /* endswitch m */ + + } /* end of MakeParm */ + +void MakeAMPM(int n) + { + char buf[8]; + int m = strlen(yytext); + +#ifdef DEBUG + fprintf(stderr, "AM: n=%d num=%d InFmt=%s OutFmt=%s;\n", + n, pp->Num, pp->InFmt, pp->OutFmt); +#endif + pp->Index[pp->Num++] = -n; + sprintf(buf, "%%%ds", m); + MakeIn(buf); + + if (pp->OutFmt) { + char *p; + + if ((p = strstr(pp->OutFmt, "%H"))) + *(p + 1) = 'I'; // 12-hour format + else if ((p = strstr(pp->OutFmt, "%#H"))) + *(p + 2) = 'I'; // 12-hour format + + MakeOut("%p"); + } /* endif Flag */ + + } /* end of MakeAMPM */ + +void MakeIn(char *text) + { + if (!pp->InFmt) + return; + + strncat(pp->InFmt, text, (pp->Outsize - 1) - strlen(pp->InFmt)); + } /* end of MakeIn */ + +void MakeOut(char *text) + { + if (!pp->OutFmt) return; + + strncat(pp->OutFmt, text, (pp->Outsize - 1) - strlen(pp->OutFmt)); + } /* end of MakeOut */ + +void Quotin(char *text) + { + if (!pp->InFmt) + return; + + MakeIn(text); + pp->InFmt[strlen(pp->InFmt)-1] = '\0'; + } /* end of Quotin */ + +void Quotout(char *text) + { + if (!pp->OutFmt) + return; + + MakeOut(text); + pp->OutFmt[strlen(pp->OutFmt)-1] = '\0'; + } /* end of Quotout */ + +int yywrap(void) + { + return 1; + } /* end of yywrap */ + diff --git a/storage/connect/frmsg1.h b/storage/connect/frmsg1.h new file mode 100644 index 00000000000..e88440a3a9c --- /dev/null +++ b/storage/connect/frmsg1.h @@ -0,0 +1,1013 @@ +#define MSG_ACCESS_VIOLATN "Violation accès mémoire" +#define MSG_ACT_ALLOC_FAIL "PlugInitLang: Erreur d'allocation du bloc Activity" +#define MSG_ADDVAL_ERROR "Erreur %d dans AddValue" +#define MSG_ADD_BAD_TYPE "Ajout d'une valeur de type %s non conforme dans un tableau %s" +#define MSG_ADD_NULL_DOM "Ajout de la chaîne %s à un domaine nul" +#define MSG_ADPOS_IN_DICTP "ADPOS au travail dans User_Dictp" +#define MSG_AFTER " après: " +#define MSG_ALG_CHOICE_AUTO "Le choix du meilleur algorithme est automatique" +#define MSG_ALG_CHOICE_BAD "Choix d'algorithme invalide, remis à AUTO" +#define MSG_ALG_CHOICE_QRY "Utilise l'algorithme 'Query'" +#define MSG_ALG_CURLY_BRK "Le choix de l'algorithme dépend des accolades externes" +#define MSG_ALLOC_ERROR "Erreur d'allocation de %s" +#define MSG_ALL_DELETED "Toutes les lignes enlevées en %.2lf sec" +#define MSG_ALTER_DB_ERR "Impossible de déterminer la base de données à modifier" +#define MSG_AMBIG_COL_QUAL "Qualificateur ambigu %s pour la colonne %s" +#define MSG_AMBIG_CORREL "Select %s.* corrélation ambigue" +#define MSG_AMBIG_SPEC_COL "Colonne spéciale ambiguë %s" +#define MSG_ANSWER_TYPE "Réponse de type" +#define MSG_API_CONF_ERROR "Erreur SQL: API_CONFORMANCE" +#define MSG_APPL_ACCESSIBLE "Application %s accessible" +#define MSG_APPL_ACTIVE "Application %s encore active" +#define MSG_APPL_BAD_SAVE "Application %s partiellement sauvegardée" +#define MSG_APPL_CREATED "Application %s crée" +#define MSG_APPL_IS_ACTIVE "Application déjà active" +#define MSG_APPL_NOT_INIT "Application non initialisée" +#define MSG_APPL_NOT_LOADED "Application non chargée" +#define MSG_APPL_QUIT "Fin de l'application %s" +#define MSG_APPL_SAVED "Application %s sauvegardée" +#define MSG_APP_STILL_ACTIV "Application du langage %s encore active (non libérable)" +#define MSG_AREAFILE_NOTFND "Fichier Area introuvable" +#define MSG_ARGS_SYNTAX_ERR "?SetArgs erreur de syntaxe: %s inattendu après %s" +#define MSG_ARG_ALREADY_SET "Argument %d déjà alloué" +#define MSG_ARG_NOT_AN_ATTR "L'argument n'est pas un attribut (type %d erroné)" +#define MSG_ARG_OUT_CONTEXT "Argument de type @ utilisé hors contexte" +#define MSG_ARG_OUT_RANGE "Argument de phrase valant %d hors limite" +#define MSG_ARG_PTR_NOSEM "Argument valant %d pointe sur un noeud sans Sem" +#define MSG_ARG_PTR_NOSEMS "Argument valant %d pointe sur un noeud sans sémantique" +#define MSG_ARG_REF_LOOP "?Bouclage entre références croisées des arguments" +#define MSG_ARG_TWO_CONST "Le 2ème argument de %s doit être constant" +#define MSG_ARRAY_ALLOC_ERR "Erreur d'allocation mémoire dans ARRAY" +#define MSG_ARRAY_BNDS_EXCD "Hors limite de tableau" +#define MSG_ARRAY_ERROR "Erreur de fonctionnement k=%d n=%d" +#define MSG_ATTRIBUTE_ERROR "Erreur règle %u attribut %s: " +#define MSG_ATT_NOT_CASE "Mauvaise valeur %d pour attribut (pas une CaseValue)" +#define MSG_ATT_POSCODE_BIG "Code attribut %d trop grand (max=%d)" +#define MSG_AVGLEN_ERROR "avglen doit être entre %d et %d" +#define MSG_BAD_AGGREG_FUNC "Fonction aggrégée %d non supportée" +#define MSG_BAD_ARGTYPES "Argument de type invalide pour %s" +#define MSG_BAD_ARGUMENTS "Argument not attachés pour %s" +#define MSG_BAD_ARG_NUM "Nombre d'arguments invalide %d" +#define MSG_BAD_ARG_TYPE "Type d'argument %d invalide" +#define MSG_BAD_ARRAY_OPER "Les tableaux doivent utiliser l'opérateur IN" +#define MSG_BAD_ARRAY_TYPE "Type=%d invalide pour un tableau" +#define MSG_BAD_ARRAY_VAL "Les tableaux doivent avoir le même nombre de valeurs" +#define MSG_BAD_BIN_FMT "Format invalide %c pour la colonne BIN %s" +#define MSG_BAD_BLK_ESTIM "Nombre de blocs supérieur à l'estimation" +#define MSG_BAD_BLK_SIZE "Taille du bloc %d non conforme" +#define MSG_BAD_BYTE_NUM "Le nombre d'octets écrits est faux" +#define MSG_BAD_BYTE_READ "Le nombre d'octets lus est faux" +#define MSG_BAD_CARDINALITY "Appel invalide de Cardinality pour une table multiple" +#define MSG_BAD_CASE_SPEC "Min/Maj: spécification %c incorrecte, recommencez: " +#define MSG_BAD_CHAR_SPEC "Spécification '%s' invalide pour caractère" +#define MSG_BAD_CHECK_TYPE "Sous-type %d invalide pour CheckColumn" +#define MSG_BAD_CHECK_VAL "Valeur pour Check invalide '%s'" +#define MSG_BAD_COLCRT_ARG "COLCRT: Arg invalide (type=%hd, domain=%hd)" +#define MSG_BAD_COLDEF_TYPE "Coldefs: type illégal %d" +#define MSG_BAD_COLIST_ITEM "Elément invalide dans une Colist" +#define MSG_BAD_COLIST_TYPE "Mauvais type=%d pour une Colist" +#define MSG_BAD_COLSIZE "Colsize %d trop petit pour cette base de données" +#define MSG_BAD_COL_ENTRY "Entrée invalide pour la colonne %s" +#define MSG_BAD_COL_FORMAT "Type de formattage %d invalide pour une colonne" +#define MSG_BAD_COL_IN_FILT "Colonne incorrecte dans un filtre" +#define MSG_BAD_COL_QUALIF "Qualificateur invalide %s pour la colonne %s" +#define MSG_BAD_COL_TYPE "Type invalide %s pour la colonne %s" +#define MSG_BAD_COL_XPATH "Xpath invalide colonne %s de la table HTML %s" +#define MSG_BAD_COMPARE_OP "Opérateur de comparaison %d invalide" +#define MSG_BAD_CONST_TYPE "Type=%d invalide pour une constante" +#define MSG_BAD_CONV_TYPE "Convertion de type invalide %d" +#define MSG_BAD_CORREL "Select %s.* corrélation absente" +#define MSG_BAD_DATETIME "Valeur date/temps invalide" +#define MSG_BAD_DATE_OPER "Opérateur de date inattendu %d" +#define MSG_BAD_DBF_FILE "Le fichier DBF %s est altéré" +#define MSG_BAD_DBF_REC "Fichier DBF %s altéré enregistrement %d" +#define MSG_BAD_DBF_TYPE "Type DBF %c non supporté" +#define MSG_BAD_DEF_ARG "Argument invalide pour INDEXDEF (type=%hd, domain=%hd)" +#define MSG_BAD_DEF_READ "EOF inattendue en lecture différée" +#define MSG_BAD_DEF_TYPE "Type de colonne invalide" +#define MSG_BAD_DIRECTORY "Répertoire invalide %s: %s" +#define MSG_BAD_DIST_JN_FIL "Filtre de jointure distincte invalide" +#define MSG_BAD_DIST_JOIN "Spécification invalide de jointure distincte" +#define MSG_BAD_DOM_COL_DEF "Définition de colonnes invalide pour un domaine" +#define MSG_BAD_DOM_VALUE "La valeur %d n'appartient pas au domaine" +#define MSG_BAD_EDIT_INIT "Coparm: édition %s initialisée improprement" +#define MSG_BAD_EVAL_TYPE "Fonction scalaire de type=%d invalide" +#define MSG_BAD_EXEC_MODE "Mode d'exécution invalide '%s'" +#define MSG_BAD_EXP_ARGTYPE "Argument de type %d invalide pour une expression" +#define MSG_BAD_EXP_OPER "Opérateur=%d invalide pour expression" +#define MSG_BAD_FETCH_RC "Code retour inattendu de Fetch %d" +#define MSG_BAD_FIELD_FMT "Format de champ invalide %c pour %s" +#define MSG_BAD_FIELD_RANK "Rang %d invalide pour la colonne %s" +#define MSG_BAD_FIELD_TYPE "Mauvais type de champ %s" +#define MSG_BAD_FILE_HANDLE "Handle de fichier invalide: %s" +#define MSG_BAD_FILE_LIST "La section liste de fichiers est erronée" +#define MSG_BAD_FILTER "Mauvais filtre: Opc=%d B_T=%d %d Type=%d %d" +#define MSG_BAD_FILTER_CONV "Conversion filtre incorrecte, B_T=%d,%d" +#define MSG_BAD_FILTER_LINK "Opérateur de chaînage illégal %d" +#define MSG_BAD_FILTER_OP "Opérateur de filtre invalide %d" +#define MSG_BAD_FILTEST_OP "Opérateur invalide %d %d pour FilTest" +#define MSG_BAD_FLD_FORMAT "Format invalide pour le champs %d de %s" +#define MSG_BAD_FLD_LENGTH "Champs %s trop long (%s --> %d) ligne %d de %s" +#define MSG_BAD_FLOAT_CONV "Convertion invalide d'un tableau flottant" +#define MSG_BAD_FPARM_NEXT "Coparm: FPARM avec Next non nul" +#define MSG_BAD_FREQ_SET "Spécification erronnée de Freq pour la colonne %s" +#define MSG_BAD_FUNC_ARG "Funcarg de type %d non implémenté" +#define MSG_BAD_FUNC_ARGTYP "Mauvais type d'argument=%d pour une fonction" +#define MSG_BAD_FUNC_MODE "%s: mode invalide %d" +#define MSG_BAD_GENRE "Genre est invalide" +#define MSG_BAD_GETVIEW_RET "GetView: type de retour %d invalide" +#define MSG_BAD_HANDLE_VAL "Valeur Handle invalide" +#define MSG_BAD_HAV_FILTER "Filtre Having sur une requête non groupée" +#define MSG_BAD_HAV_FILTYPE "Filtre invalide pour clause Having" +#define MSG_BAD_HEADER "Fichier %s: bloc en-tête altéré" +#define MSG_BAD_HEADER_VAL "Valeur invalide pour Header" +#define MSG_BAD_HEAD_END "Lecture fin d'en-tête impossible" +#define MSG_BAD_INDEX_COL "Colonne %s invalide pour index %s" +#define MSG_BAD_INDEX_DEF "Définition invalide pour index %s" +#define MSG_BAD_INDEX_FILE "Fichier index %s corrompu" +#define MSG_BAD_INDEX_PART "Définition colonne invalide pour index %s" +#define MSG_BAD_INPUT "Entrée incorrecte" +#define MSG_BAD_IN_ARGTYPE "Argument de type invalide pour l'opérateur IN" +#define MSG_BAD_IN_ENDING "Erreur: fin de chaîne IN invalide" +#define MSG_BAD_IN_STRING "La chaîne IN commence ou finie par des caractères invalides %c ... %c" +#define MSG_BAD_JCOL_TYPE "Erreur logique JCT: disparité des types colonnes" +#define MSG_BAD_JOIN_EXP "Expression invalide pour une jointure" +#define MSG_BAD_JOIN_FILTER "Filtre de jointure invalide" +#define MSG_BAD_JOIN_OP "Opérateur de joint invalide %d" +#define MSG_BAD_LANG_SIZE "Le fichier langage a une mauvaise taille %d" +#define MSG_BAD_LINEFLD_FMT "Format invalide ligne %d champs %d de %s" +#define MSG_BAD_LINE_LEN "Longueur ligne non égale à Lrecl" +#define MSG_BAD_LIST_TYPE "Type de liste invalide %d" +#define MSG_BAD_LOCALE "Locale invalide %s" +#define MSG_BAD_LOCDFON_ARG "Mauvais paramètre pour LOCDFON" +#define MSG_BAD_LOCNODE_USE "Usage inattendu de LOCNODE" +#define MSG_BAD_LRECL "Disparité lrecl table/fichier (%d,%hd)" +#define MSG_BAD_MAX_HAVING "MAXTMP trop petit pour Having" +#define MSG_BAD_MAX_NREC "MaxRec=%d ne correspond pas à MaxBlk=%d Nrec=%d" +#define MSG_BAD_MAX_PARAM "Mauvais paramètres pour spécifier une valeur maximum" +#define MSG_BAD_MAX_SETTING "Mauvaise valeur '%c' pour max" +#define MSG_BAD_MERGE_TYPE "Le type %d ne pas être intercallé" +#define MSG_BAD_NODE_TYPE "Type noeud erroné pour la table" +#define MSG_BAD_OFFSET_VAL "Nul offset invalide pour une table CSV" +#define MSG_BAD_OPEN_MODE "Mode d'ouverture invalide %d" +#define MSG_BAD_OPERATOR "Opérateur invalide %s" +#define MSG_BAD_ORDER_MODE "Mode de tri %c invalide" +#define MSG_BAD_ORDER_TYPE "Tri sur objet de type=%d invalide" +#define MSG_BAD_OUTER_JOIN "Jointure externe invalide sur table enfant" +#define MSG_BAD_PAD_ARGTYP "Argument de type invalide pour Pad ou Justify" +#define MSG_BAD_PARAMETERS "%.8s: Mauvais paramètres" +#define MSG_BAD_PARAM_TYPE "%.8s: Paramètre de type=%d invalide" +#define MSG_BAD_PARM_COUNT "Nombre de paramètres incohérent" +#define MSG_BAD_PHASE_NUM "Numéro de phrase %d hors limite" +#define MSG_BAD_PHRASE_NB "numéro de phrase hors limite %d rc=%d\n" +#define MSG_BAD_POS_CODE "POS_code invalide %d" +#define MSG_BAD_POS_TYPE "Type de POS_code invalide %d" +#define MSG_BAD_PROJNUM "Mauvais projnum %d pour la colonne %s" +#define MSG_BAD_QUERY_OPEN "Mode invalide %d pour l'ouverture d'une requête" +#define MSG_BAD_QUERY_TYPE "Type de requête %d invalide pour %s" +#define MSG_BAD_QUOTE_FIELD "Quote manquante dans %s champs %d ligne %d" +#define MSG_BAD_READ_NUMBER "Mauvais nombre %d de valeurs lues dans %s" +#define MSG_BAD_RECFM "Recfm type %d invalide pour DOSCOL" +#define MSG_BAD_RECFM_VAL "Valeur invalide %d de Recfm" +#define MSG_BAD_RESULT_TYPE "Mauvais type de résultat %d pour %s" +#define MSG_BAD_RETURN_TYPE "Type de retour %d incorrect" +#define MSG_BAD_ROW_VALIST "Liste de valeurs invalide pour ROW" +#define MSG_BAD_ROW_VALNB "Nombre de valeurs inégal dans la liste" +#define MSG_BAD_SCF_ARGTYPE "Argument %d de type=%s invalide pour %s" +#define MSG_BAD_SEM_DOMAIN "Domain .%d invalide" +#define MSG_BAD_SETTINGS "Certaines spécifications sont incompatibles avec le type de la table" +#define MSG_BAD_SET_CASE "La casse d'un tableau ne peut pas passer de non respect à respecter" +#define MSG_BAD_SET_STRING "SetValue: appel invalide pour STRING" +#define MSG_BAD_SET_TYPE "Set type %hd invalide" +#define MSG_BAD_SPECIAL_CMD "Commande spéciale invalide" +#define MSG_BAD_SPECIAL_COL "Colonne spéciale invalide %s" +#define MSG_BAD_SPEC_COLUMN "Colonne spéciale invalide pour ce type de table" +#define MSG_BAD_SQL_PARAM "Paramètre SQL invalide pour FindColblk" +#define MSG_BAD_SUBLST_TYPE "Coparm: type %d de sous-liste invalide" +#define MSG_BAD_SUBSEL_IN_X "Sub-select invalide pour une expression" +#define MSG_BAD_SUBSEL_TYPE "Type %d invalide retourné de Sub-Select" +#define MSG_BAD_SUB_RESULT "Résultat indéfini de fonction Sub-Select" +#define MSG_BAD_SUB_SELECT "Sub-select invalide comme argument de fonction" +#define MSG_BAD_TABLE_LINE "Ligne '%s' illégale ou tronquée dans la section Tables" +#define MSG_BAD_TABLE_LIST "Table %s absente de la liste des tables" +#define MSG_BAD_TABLE_TYPE "Type invalide %s pour la table %s" +#define MSG_BAD_TEST_TYPE "BlockTest sur tableau: types dépareillés %s %s" +#define MSG_BAD_TRIM_ARGTYP "Argument de type invalide pour Trim" +#define MSG_BAD_TYPE_FOR_IN "Types d'argument incompatibles pour la fonction IN" +#define MSG_BAD_TYPE_FOR_S "Type incorrecte %d pour %s(%d)" +#define MSG_BAD_TYPE_LIKE "Type(%d)= %d invalide pour LIKE" +#define MSG_BAD_UPD_COR "Le qualificateur %s de la colonne %s ne se refère pas à la table mise à jour %s" +#define MSG_BAD_USERBLK_LEN "Mauvaise longueur à l'écriture du bloc utilisateur" +#define MSG_BAD_USETEMP "Usetemp invalide '%s'" +#define MSG_BAD_USETEMP_VAL "Valeur pour Usetemp invalide %d" +#define MSG_BAD_VALBLK_INDX "Valeur hors limites de l'index du bloc de valeurs" +#define MSG_BAD_VALBLK_TYPE "Type=%d invalide pour un bloc de valeurs" +#define MSG_BAD_VALNODE "Type %d invalide pour le noeud valeur colonne %s" +#define MSG_BAD_VALUE_TYPE "Type de valeur invalide %d" +#define MSG_BAD_VAL_UPDATE "Impossible de déterminer quelle valeur %s doit être mise à jour" +#define MSG_BAD_VIEW_OPEN "Mode invalide %d pour l'ouverture d'une View" +#define MSG_BAD_XMODE_VAL "Mode d'exécution %d invalide" +#define MSG_BAD_XOBJ_TYPE "Mauvais type de Xobject %d" +#define MSG_BAS_NS_LIST "Format invalide de la liste des espace-noms" +#define MSG_BIN_F_TOO_LONG "Valeur trop longue pour le champ %s (%d --> %d)" +#define MSG_BIN_MODE_FAIL "Echec mode binaire: %s" +#define MSG_BLKTYPLEN_MISM "Disparité types/longueurs de bloc dans SetValue" +#define MSG_BLK_IS_NULL "Blk est nul" +#define MSG_BLOCK_NO_MATCH "Bloc non correspondant" +#define MSG_BREAKPOINT "Point de contrôle" +#define MSG_BUFF_TOO_SMALL "GetColData: Buffer trop petit" +#define MSG_BUFSIZE_ERROR "Erreur en recherchant la taille du buffer" +#define MSG_BUILDING_GROUPS "Formation des groupes" +#define MSG_BUILD_DIST_GRPS "Formation des groupes distinctes" +#define MSG_BUILD_INDEX "Construction index %s sur %s" +#define MSG_BXP_NULL "Bxp nul dans PUTFON" +#define MSG_CANNOT_OPEN "Ouverture impossible de %s" +#define MSG_CD_ONE_STEP "Count Distinct doit être exécuté en une seule étape" +#define MSG_CD_ORDER_ERROR "Erreur de tri dans Count Distinct" +#define MSG_CHECKING_ROWS "Test des lignes à mettre à jour" +#define MSG_CHECK_LEVEL "Niveau de vérification fixé à %u" +#define MSG_CHSIZE_ERROR "Erreur dans chsize: %s" +#define MSG_CLN_NOT_IN_JOIN "La colonne C%d n'est pas dans le join" +#define MSG_CNTDIS_COL_LOST "Colonne du Count Distinct perdue" +#define MSG_COLIST_BAD_TYPE "Type=%d invalide pour Colist" +#define MSG_COLNAM_TOO_LONG "Nom de colonne trop long" +#define MSG_COLSEC_TOO_BIG "Section colonne trop grande, table %s (%d)" +#define MSG_COLS_REDUCED " (réduit par Maxcol)" +#define MSG_COLUMN_ERROR "Erreur de colonne" +#define MSG_COLUMN_MISMATCH "Colonne %s dépareillée" +#define MSG_COLUMN_NOT_KEY "La colonne jointe R%d.%s n'est pas une clé" +#define MSG_COL_ALLOC_ERR "Allocation impossible du noeud colonne" +#define MSG_COL_ALLOC_ERROR "Erreur d'allocation mémoire pour la colonne %d" +#define MSG_COL_HAS_NO_DEF "La colonne %s n'est pas définie" +#define MSG_COL_INVAL_TABLE "La colonne %s.%s n'existe pas dans la table %s alias %s" +#define MSG_COL_ISNOT_TABLE "La colonne %s n'est pas dans la table %s" +#define MSG_COL_NB_MISM "Le nombre de colonnes ne correspond pas" +#define MSG_COL_NOTIN_GRPBY "La colonne %s n'est pas dans la liste de Group By" +#define MSG_COL_NOTIN_TABLE "La colonne %s n'est dans aucune table" +#define MSG_COL_NOTIN_UPDT "%s n'appartient pas à la table mise à jour %s" +#define MSG_COL_NOT_CODED "La colonne %s n'est pas codifiée" +#define MSG_COL_NOT_EXIST "La colonne %s n'existe pas dans %s" +#define MSG_COL_NOT_FOUND "La colonne %s n'est pas dans la table %s" +#define MSG_COL_NOT_IN_DB "La colonne %s de la table %s n'est pas dans la base de données" +#define MSG_COL_NOT_IN_JOIN "La colonne %s n'est pas dans le join" +#define MSG_COL_NOT_SORTED "La colonne %s de la table %s n'est pas triée" +#define MSG_COL_NUM_MISM "Disparité du nombre de colonnes" +#define MSG_COL_USED_TWICE "Colonne %s utilisée deux fois ???" +#define MSG_COMPUTE_ERROR "Erreur dans Compute, op=%d" +#define MSG_COMPUTE_NIY "Compute non implémenté pour TOKEN" +#define MSG_COMPUTING "Calculs en cours" +#define MSG_COMPUTING_DIST "Comptage des valeurs distinctes" +#define MSG_COMPUTING_FUNC "Calcul de(s) fonction(s)" +#define MSG_COM_ERROR "Erreur Com" +#define MSG_CONCAT_SUBNODE "Concaténation de sous-noeuds impossible" +#define MSG_CONNECTED "Connecté" +#define MSG_CONNECT_CANCEL "Connection interrompue par l'utilisateur" +#define MSG_CONNECT_ERROR "Erreur %d se connectant à %s" +#define MSG_CONN_CLOSED "%s(%d) fermée" +#define MSG_CONN_CREATED "Connexion %s crée" +#define MSG_CONN_DROPPED "Connexion %s supprimée" +#define MSG_CONN_OPEN "%s(%d) ouverte (%s)" +#define MSG_CONN_SUC_OPEN "%s(%d) ouverte avec succès" +#define MSG_CONTROL_C_EXIT "Exit par Ctrl-C" +#define MSG_COPY_BAD_PHASE "Copie de liste invalide en phase %d" +#define MSG_COPY_INV_TYPE "Coparm: type non supporté %d" +#define MSG_CORREL_NO_QRY "Les sous-requêtes corrélées ne peuvent pas être de type QRY" +#define MSG_CREATED_PLUGDB " Créé par PlugDB %s " +#define MSG_CURSOR_SET "Curseur remis à %d" +#define MSG_DATABASE_ACTIVE "Base de données %s activée" +#define MSG_DATABASE_LOADED "Base de données %s chargée" +#define MSG_DATA_IS_NULL "ExecSpecialCmd: data est NULL" +#define MSG_DATA_MISALIGN "Mauvais alignement pour ce type de données" +#define MSG_DBASE_FILE "Fichier dBASE dbf: " +#define MSG_DB_ALREADY_DEF "Base de données %s déjà définie" +#define MSG_DB_ALTERED "Base de données modifiée" +#define MSG_DB_CREATED "Base de données %s créée" +#define MSG_DB_NOT_SPEC "Base de données non spécifiée" +#define MSG_DB_REMOVED "Base de données %s retirée de la liste" +#define MSG_DB_SORT_ERROR "Erreur de tri DB" +#define MSG_DB_STOPPED "Arrêt de la base de données %s" +#define MSG_DEBUG_NOT_ACTIV "Mode Debug inactif" +#define MSG_DEBUG_SET_INV "Invalide pour Debug: %c" +#define MSG_DEF_ALLOC_ERROR "Erreur d'allocation de la classe DEF %s" +#define MSG_DELETING_ROWS "Suppression des lignes" +#define MSG_DEL_FILE_ERR "Erreur à l'effacement de %s" +#define MSG_DEL_READ_ERROR "Delete: erreur en lecture req=%d len=%d" +#define MSG_DEL_WRITE_ERROR "Delete: erreur en écriture: %s" +#define MSG_DEPREC_FLAG "Option Flag périmée, utiliser Coltype" +#define MSG_DICTIONARY "Dictionnaire " +#define MSG_DIRECT_VARTOK "Accès direct aux règles du Variable Token non implémenté" +#define MSG_DISCONNECTED "Déconnecté" +#define MSG_DISTINCT_ERROR "Plus d'un élément fonctionel DISTINCT" +#define MSG_DISTINCT_ROWS "Sélection des lignes distinctes" +#define MSG_DISTINCT_VALUES "Extraction des valeurs distinctes" +#define MSG_DIS_NOHEAD_JOIN "Jointure distincte sur une table non en tête" +#define MSG_DLL_LOAD_ERROR "Erreur %d au chargement du module %s" +#define MSG_DOMAIN_EMPTY "Le domaine %s est vide" +#define MSG_DOMAIN_ERROR "Colonne %s: disparité domaine(%s)/valeur(%s)" +#define MSG_DOMAIN_FULL "Le domaine %s est plein (max=%d)" +#define MSG_DOM_FILE_ERROR "Fichier domain %s introuvable" +#define MSG_DOM_NOT_SUPP "MS-DOM non supporté par cette version" +#define MSG_DOM_OPEN_ERROR "Erreur d'ouverture du domaine: %s" +#define MSG_DOM_READ_ERROR "Erreur %d en lecture de domaine: %s" +#define MSG_DOM_READ_ONLY "La table domaine %s est en lecture seulement" +#define MSG_DOM_WRITE_ERROR "Erreur %d en écriture de domaine: %s" +#define MSG_DONE "Effectué, rc=%d" +#define MSG_DOSALMEM_NOMEM "Erreur d'allocation, pas assez de mémoire" +#define MSG_DROP_DB_ERR "Echec du Drop sur le base de données %s" +#define MSG_DSORT_LOG_ERROR "Kindex: Erreur logique de tri distincte" +#define MSG_DUMMY_NO_COLS "Les tables DUMMY ne peuvent pas avoir de colonne" +#define MSG_DUPLICAT_COUNT "Count sur plus d'une colonne" +#define MSG_DUP_COL_NAME "La colonne %s existe en double" +#define MSG_DUP_PROJNUM "Non unique projnum %d pour la colonne %s" +#define MSG_DVAL_NOTIN_LIST "Valeur %s non trouvée dans la liste des valeurs distinctes de la colonne %s" +#define MSG_EMPTY_DOC "Document vide" +#define MSG_EMPTY_FILE "%s du fichier vide %s: " +#define MSG_ENDSTR_MISMATCH "Fins de chaîne et de noeud ne correspondent pas" +#define MSG_END_OF_DELETE "%d ligne(s) enlevée(s) en %.2lf sec" +#define MSG_END_OF_INSERT "%d ligne(s) insérée(s) en %.2lf sec" +#define MSG_END_OF_QUERY "%d ligne(s) extraite(s) en %.2lf sec" +#define MSG_END_OF_UPDATE "%d ligne(s) modifiée(s) en %.2lf sec" +#define MSG_EOF_AFTER_LINE "Fin de fichier après la ligne %d" +#define MSG_EOF_INDEX_FILE "EOF lisant le fichier index" +#define MSG_ERASED " et effacée" +#define MSG_ERASE_FAILED " (échec de l'effacement)" +#define MSG_ERROR "Erreur" +#define MSG_ERROR_IN_LSK "Erreur %d dans lseek64" +#define MSG_ERROR_IN_SFP "Erreur %d dans SetFilePointer" +#define MSG_ERROR_NO_PARM "Paramètre absent (valide seulement pour %.8s.1 et %.8s.5)" +#define MSG_ERROR_OPENING "Erreur à l'ouverture de : " +#define MSG_ERR_NUM_GT_MAX "Erreur: Numval (%d) plus grand que Maxnum (%d)" +#define MSG_ERR_READING_REC "Erreur lisant l'enregistrement %d de %s" +#define MSG_ERR_RET_RULE "Retour erreur, règle=%u" +#define MSG_ERR_RET_TYPE "Retour erreur, type=%d" +#define MSG_EVAL_EXPIRED "Cette version d'évaluation est expirée" +#define MSG_EVAL_ONLY "L'utilisation de cette Dll est pour évaluation seulement" +#define MSG_EXECUTING "Exécution" +#define MSG_EXECUTION_ERROR "Erreur d'exécution" +#define MSG_EXEC_MODE_IS "Le mode d'exécution est %s" +#define MSG_EXEC_MODE_RESET ". Mode remis à Execute" +#define MSG_EXEC_MODE_SET "Mode d'exécution fixé à %s" +#define MSG_EXIT_EVAL_ERR "Erreur pendant l'évaluation de Exit" +#define MSG_EXIT_FROM_LANG "Fin du langage %s version %d.%d" +#define MSG_FAIL_ADD_NODE "L'ajout du noeud %s dans la table a échoué" +#define MSG_FETCHING_DATA "Recherche des données" +#define MSG_FETCHING_ROWS "Recherche des lignes" +#define MSG_FETCH_NO_RES "Fetch: Pas de Résultats" +#define MSG_FIELD_TOO_LONG "Valeur trop longue pour le champs %d ligne %d" +#define MSG_FILELEN_ERROR "Erreur dans %s pour %s" +#define MSG_FILE_CLOSE_ERR "Erreur %d à la fermeture du fichier" +#define MSG_FILE_IS_EMPTY "Le fichier %s est vide" +#define MSG_FILE_MAP_ERR "Erreur de File mapping" +#define MSG_FILE_MAP_ERROR "CreateFileMapping %s erreur rc=%d" +#define MSG_FILE_NOT_FOUND "Fichier %s introuvable" +#define MSG_FILE_OPEN_YET "Fichier %s déjà ouvert" +#define MSG_FILE_UNFOUND "Fichier %s non trouvé" +#define MSG_FILGRP_NO_TABLE "Table %d manquante pour groupe filtre" +#define MSG_FILTER_ATTACH "Filtre passé à Attach" +#define MSG_FILTER_NO_TABLE "Filtre: première table manquante" +#define MSG_FIND_BAD_TYPE "Recherche dans un tableau: type non conforme %s %s" +#define MSG_FIX_OVFLW_ADD "Dépassement de capacité en addition" +#define MSG_FIX_OVFLW_TIMES "Dépassement de capacité en mutiplication" +#define MSG_FIX_UNFLW_ADD "Sous dépassement de capacité en addition" +#define MSG_FIX_UNFLW_TIMES "Sous dépassement de capacité en multiplication" +#define MSG_FLD_TOO_LNG_FOR "Champs %d trop long pour %s ligne %d de %s" +#define MSG_FLTST_NO_CORREL "FilTest ne devrait être appelé que pour les sous-requêtes corrélées" +#define MSG_FLT_BAD_RESULT "Virgule flottante: résultat inexacte" +#define MSG_FLT_DENORMAL_OP "Opérande virgule flottante non normalisé" +#define MSG_FLT_INVALID_OP "Opération virgule flottante invalide" +#define MSG_FLT_OVERFLOW "Dépassement de capacité virgule flottante" +#define MSG_FLT_STACK_CHECK "Virgule flottante: Erreur de la pile" +#define MSG_FLT_UNDERFLOW "Sous-dépassement de capacité virgule flottante" +#define MSG_FLT_ZERO_DIVIDE "Virgule flottante: division par zéro" +#define MSG_FMT_WRITE_NIY "L'écriture des fichiers %s n'est pas encore implémentée" +#define MSG_FNC_NOTIN_SLIST "Fonction de tri absente de la liste de sélection" +#define MSG_FORMAT_ERROR "Erreur de formattage" +#define MSG_FOXPRO_FILE "Fichier FoxPro: " +#define MSG_FPUTS_ERROR "Erreur dans fputs: %s" +#define MSG_FSBPARP_NULL "PUTFON: fsbparp est nul" +#define MSG_FSEEK_ERROR "Erreur dans fseek: %s" +#define MSG_FSETPOS_ERROR "Erreur dans fseek pour i=%d" +#define MSG_FTELL_ERROR "Erreur dans ftell enregistrement=%d: %s" +#define MSG_FUNCTION_ERROR "Erreur dans %s: %d" +#define MSG_FUNC_ERRNO "Erreur %d dans %s" +#define MSG_FUNC_ERROR "Erreur dans %s" +#define MSG_FUNC_ERR_S "Erreur dans %s: %s" +#define MSG_FUNC_REF_DEL "Référence à une fonction définie (règle %d) qui a été supprimée" +#define MSG_FWRITE_ERROR "Erreur dans fwrite: %s" +#define MSG_GETCWD_ERR_NO "?getcwd %s errno=%d" +#define MSG_GETFILESIZE_ERR "Erreur %d dans GetFileSize" +#define MSG_GET_DIST_VALS "Récupération des valeurs distinctes de " +#define MSG_GET_ERROR "Erreur dans %s (colonne %d)" +#define MSG_GET_FUNC_ERR "Erreur en recherche de la fonction %s: %s" +#define MSG_GET_NAME_ERR "Erreur en retrouvant le nom d'une table SYS" +#define MSG_GLOBAL_ERROR "Erreur d'allocation de Global (taille=%d)\n" +#define MSG_GRAM_ALLOC_ERR "Erreur d'allocation dans Grammar Up" +#define MSG_GRAM_MISMATCH "Avertissement: version de GRAMMAR perimée (sauvé sous GRAMMAR v%u)" +#define MSG_GRAM_SUBSET_ERR "Erreur d'initialisation du dictionnaire de la grammaire" +#define MSG_GRBY_TAB_NOTIMP "Group by avec tables jointes non implémenté" +#define MSG_GROUPBY_NOT_ALL "Group By doit inclure toutes les sélections non-fonctionnelles" +#define MSG_GROUP_ON_FUNC "Group by invalide sur colonne fonctionnelle" +#define MSG_GRP_COL_MISM "Disparité colonne des groupes" +#define MSG_GRP_LIST_MISMAT "Le groupement ne couvre pas la liste de sélection" +#define MSG_GUARD_PAGE "Violation de page de garde" +#define MSG_GZOPEN_ERROR "gzopen %s: erreur %d sur %s" +#define MSG_GZPUTS_ERROR "Erreur dans gzputs: %s" +#define MSG_HANDLE_IS_NULL "%s est NULL: erreur code: %d" +#define MSG_HARRY_COMP_NIY "Compute non implémenté pour les chaînes codées" +#define MSG_HAVING_FILTER "Traitement du Filtre Having" +#define MSG_HBUF_TOO_SMALL "Buffer(%d) trop petit pour entête(%d)" +#define MSG_HEAD_OPEN_ERROR "Erreur à l'ouverture du fichier header" +#define MSG_HEAD_READ_ERROR "Erreur en lecture du fichier header %s" +#define MSG_HEAD_WRITE_ERR "Erreur en écriture du fichier header" +#define MSG_HI_OFFSET_ERR "Offset supérieur non nul" +#define MSG_HUGE_DEFAULT "Huge est %d par défault" +#define MSG_HUGE_WARNING_1 "Mémoire Huge non compatible 16-bit pour %d\n" +#define MSG_HUGE_WARNING_2 "Résultats imprévisibles possibles\n" +#define MSG_IDLE "Au repos" +#define MSG_ILLEGAL_INSTR "Instruction illégale" +#define MSG_ILL_FILTER_CONV "Conversion implicite illégale dans un filtre" +#define MSG_INDEX_CREATED "Index %s créé sur %s" +#define MSG_INDEX_DEF_ERR "Erreur sauvegardant l'index définition pour %s" +#define MSG_INDEX_DROPPED "Index %s supprimé de %s" +#define MSG_INDEX_INIT_ERR "Echec de l'initialisation de l'index %s" +#define MSG_INDEX_NOT_DEF "Index %s non défini" +#define MSG_INDEX_NOT_UNIQ "L'index n'est pas Unique" +#define MSG_INDEX_ONE_SAVE "Les index sont sauvegardés dans un fichier unique" +#define MSG_INDEX_SEP_SAVE "Les index sont sauvegardés dans des fichiers séparés" +#define MSG_INDEX_YET_ON "L'index %s existe déjà sur %s" +#define MSG_INDX_ALL_DROP "Tous les index de %s supprimés" +#define MSG_INDX_COL_NOTIN "La colonne index %s n'existe pas dans la table %s" +#define MSG_INDX_EXIST_YET "L'entrée index existe déjà" +#define MSG_INIT_ERROR "Erreur à l'initialisation de %s" +#define MSG_INIT_FAILED "L'initialisation de %s a échoué" +#define MSG_INPUT "Entrée: " +#define MSG_INPUT_KEYBD_YET "L'entrée est déjà au clavier" +#define MSG_INSERTING "Insertion: " +#define MSG_INSERT_ERROR "Insert erreur: usage multiple du fichier %s" +#define MSG_INSERT_MISMATCH "Les listes colonne et valeur ne correspondent pas" +#define MSG_INTERNAL "interne" +#define MSG_INT_COL_ERROR "Erreur interne sur la colonne index %s" +#define MSG_INT_OVERFLOW "Dépassement de capacité sur entier" +#define MSG_INT_ZERO_DIVIDE "Division entière par zéro" +#define MSG_INVALID_BIP "Bip invalide .%d" +#define MSG_INVALID_DISP "Disposition invalide" +#define MSG_INVALID_FTYPE "SBV: Ftype %d invalide" +#define MSG_INVALID_HANDLE "Poignée invalide" +#define MSG_INVALID_OPER "Opérateur invalide %d pour %s" +#define MSG_INVALID_OPTION "Option invalide %s" +#define MSG_INV_COLUMN_TYPE "Type %d Invalide pour la colonne %s" +#define MSG_INV_COL_DATATYP "Type de données %d invalide pour la colonne %d" +#define MSG_INV_COL_NUM "Colonne invalide %d" +#define MSG_INV_COL_TYPE "Type de colonne %s invalide" +#define MSG_INV_CONC_BIP "Bip invalide (seuls valides: %.8s.0 .1 and .5)" +#define MSG_INV_DATA_PATH "Chemin vers les données invalide" +#define MSG_INV_DEF_READ "Lecture différée invalide rc=%d" +#define MSG_INV_DIRCOL_OFST "Offset invalide pour une colonne DIR" +#define MSG_INV_DOMAIN_TYPE "Type invalide %d" +#define MSG_INV_FILTER "Filtre résiduel dans %s" +#define MSG_INV_FNC_BUFTYPE "FNC: Type %d de l'argument invalide pour %s" +#define MSG_INV_INFO_TYPE "Type d'info catalog invalide %d" +#define MSG_INV_INIPATH "Inipath invalide " +#define MSG_INV_MAP_POS "Position mémoire invalide" +#define MSG_INV_OPERATOR "opérateur invalide %d\n" +#define MSG_INV_PARAMETER "Paramètre invalide %s" +#define MSG_INV_PARM_TYPE "Type de paramètre invalide" +#define MSG_INV_QUALIFIER "Qalificateur '%s' invalide" +#define MSG_INV_QUERY_TYPE "Type de requête %d invalide" +#define MSG_INV_RAND_ACC "L'accès aléatoire d'une table non optimisée est impossible" +#define MSG_INV_REC_POS "Position d'enregistrement invalide" +#define MSG_INV_RESULT_TYPE "Type de résultat invalide %s" +#define MSG_INV_SET_SUBTYPE "Type de formattage %d invalide" +#define MSG_INV_SPECIAL_CMD "%s: Commande spéciale invalide" +#define MSG_INV_SUBTYPE "Sous type invalide %s" +#define MSG_INV_TOK_DOMAIN "Le domaine %s n'existe pas" +#define MSG_INV_TOPSEM_CMD "Commande TopSem invalide %c" +#define MSG_INV_TRANSF_USE "Usage invalide en règle transformationnelle" +#define MSG_INV_TYPE_SPEC "Spécification de type invalide (%.8s.%d)" +#define MSG_INV_UPDT_TABLE "Table %s invalide pour Update" +#define MSG_INV_VALUE_LIST "Liste de valeurs invalide pour Insert" +#define MSG_INV_WHERE_JOIN "Clause Where invalide dans une requête de jointure" +#define MSG_INV_WORK_PATH "Chemin de travail invalide" +#define MSG_IN_ARGTYPE_MISM "Arguments de types incompatibles pour une expression IN" +#define MSG_IN_USE " et en activité" +#define MSG_IN_WITHOUT_SUB "IN ou EXISTS sans tableau ou subquery" +#define MSG_IS_NOT_CONN "%s n'est pas une connexion définie" +#define MSG_JCT_MISS_COLS "Colonnes manquantes pour une table JCT" +#define MSG_JCT_MISS_TABLE "Table jointe manquante pour JCT" +#define MSG_JCT_NO_FILTER "Filtrage impossible des tables virtuelles JCT" +#define MSG_JCT_NO_KEY "Erreur logique JCT: clé manquante" +#define MSG_JOIN_KEY_NO_COL "La clé de jointure n'est pas une colonne" +#define MSG_KEY_ALLOC_ERR "Erreur d'allocation d'un bloc offset clé" +#define MSG_KEY_ALLOC_ERROR "Erreur d'allocation mémoire, Klen=%d n=%d" +#define MSG_LANGUAGE_QUIT "%s libéré" +#define MSG_LANG_ACTIVE "Langage %s actif" +#define MSG_LANG_ALLOC_FAIL "PlugInitLang: Erreur d'allocation du bloc Lang" +#define MSG_LANG_ALREADY_UP "Langage déjà en édition" +#define MSG_LANG_BAD_SAVE "Langage %s peut-être incorrectement sauvegardé" +#define MSG_LANG_NOT_FREED "Langage %s non libérable (pas dans la chaîne principale)" +#define MSG_LANG_SAVED "Langage %s sauvegardé" +#define MSG_LANG_WR_LEN_ERR "Erreur de longueur à l'écriture du bloc Lang" +#define MSG_LDF_ALLOC_ERROR "Erreur d'allocation d'un LdfBlock" +#define MSG_LDF_RN_MISMATCH "LDF: décalage des numéros de règle" +#define MSG_LDF_WLEN_ERROR "Erreur de longueur en écrivant LdfData" +#define MSG_LDF_W_LEN_ERROR "Erreur de longueur pour LdfData en écriture" +#define MSG_LIC_NO_MYSQL "Votre licence actuelle ne permet pas l'utilisation du type MYSQL" +#define MSG_LINEAR_ERROR "Erreur de linéarisation" +#define MSG_LINE_LENGTH "Largeur d'impression fixée à %d" +#define MSG_LINE_MAXLIN "Nombre de lignes de travail plafonné à %d" +#define MSG_LINE_MAXRES "Nombre de lignes de résultat plafonné à %d" +#define MSG_LINE_MAXTMP "Nombre de lignes intermédiaires plafonné à %d" +#define MSG_LINE_TOO_LONG "La nouvelle ligne est trop longue" +#define MSG_LINJOINDB_ERROR "Erreur système: appel incorrecte à LinJoinDB" +#define MSG_LIST "--Liste--" +#define MSG_LNG_NOT_IN_LIST "Le langage %s n'est pas dans la liste" +#define MSG_LOADING_DB "Chargement description de la BD" +#define MSG_LOADING_FAILED "Le chargement de %s a échoué" +#define MSG_LOAD_CDLL_ERROR "Erreur au chargement de ConnDll: rc=%d" +#define MSG_LOCSTRG_TOO_BIG "LOCSTRG: n trop grand ? (%d)\n" +#define MSG_LOGICAL_ERROR "%s: Erreur logique" +#define MSG_LRECL_TOO_SMALL "Lrecl trop petit (longueur en-tête = %d)" +#define MSG_MAC_NO_DELETE "Pas de suppression de lignes pour les tables MAC" +#define MSG_MAC_NO_INDEX "Pas d'accès direct aux tables MAC" +#define MSG_MAC_READ_ONLY "Les tables MAC sont en lecture seulement" +#define MSG_MAC_WIN_ONLY "Les tables MAC sont seulement sous Windows" +#define MSG_MAKE_EMPTY_FILE "Génération du fichier vide %s: %s" +#define MSG_MAKING "Génération" +#define MSG_MAKING_DISTINCT "Regroupement des valeures distinctes" +#define MSG_MALLOC_ERROR "Allocation mémoire impossible par %s" +#define MSG_MALLOC_NULL "malloc retourne Null" +#define MSG_MAP_NO_MORE "Le type %s n'est plus supporté" +#define MSG_MAP_OBJ_ERR "Erreur %d à la fermeture du map objet" +#define MSG_MAP_VEC_ONLY "MAP Insert permis seulement pour les tables VEC Estimate" +#define MSG_MAP_VIEW_ERROR "MapViewOfFile %s erreur rc=%d" +#define MSG_MAXSIZE_ERROR "Maxsize incalculable sur table ouverte" +#define MSG_MAXTMP_TRUNCATE "Résultats intermédiaires tronqués par maxtmp=%d" +#define MSG_MAX_BITMAP "Taille maxi des bitmaps d'optimisation fixée à %d" +#define MSG_MEMSIZE_TOO_BIG "Erreur: memsize (%d) trop grand pour Length (%d)" +#define MSG_MEM_ALLOC_ERR "Erreur d'allocation mémoire, taille %s = %d" +#define MSG_MEM_ALLOC_ERROR "Erreur d'allocation mémoire" +#define MSG_MEM_ALLOC_YET "Mémoire déjà allouée" +#define MSG_METAFILE_NOTFND "Fichier Meta introuvable" +#define MSG_MISPLACED_QUOTE "Appostrophe mal placée ligne %d" +#define MSG_MISSING "Manquant: Value=%p Argval=%p Builtin=%d" +#define MSG_MISSING_ARG "Argument manquant pour l'opérateur %d" +#define MSG_MISSING_COL_DEF "Définition des colonnes manquante" +#define MSG_MISSING_CONNECT "Connection #1 manquante" +#define MSG_MISSING_EOL "Fin de ligne manquante dans %s" +#define MSG_MISSING_FIELD "Champs %d manquant dans %s ligne %d" +#define MSG_MISSING_FNAME "Nom du fichier manquant" +#define MSG_MISSING_NODE "Noeud %s manquant dans %s" +#define MSG_MISSING_POS "POS code manquant" +#define MSG_MISSING_ROWNODE "Impossible de trouver le noeud de la ligne %d" +#define MSG_MISSING_SERV_DB "Indication serveur et/ou base de données manquante" +#define MSG_MISS_LEAD_COL "Colonne majeure %s manquante" +#define MSG_MISS_NAME_LRECL "Nom du fichier et/ou LRECL manquant" +#define MSG_MISS_TABLE_LIST "Liste des tables manquante" +#define MSG_MISS_VCT_ELMT "Taille de bloc vectoriel manquante (Elements)" +#define MSG_MIS_TAG_LIST "Liste des balises colonne manquante" +#define MSG_MKEMPTY_NIY "MakeEmptyFile: pas encore implementé pour Huge et Unix" +#define MSG_MOVE_INV_TYPE "MOVPARM: paramètre de type invalide %d" +#define MSG_MULT_DISTINCT "Distinct utilisé plus d'une fois" +#define MSG_MULT_KEY_ERROR "Erreur sur clé multiple k=%d n=%d" +#define MSG_MUL_MAKECOL_ERR "Erreur logique dans TABMUL::MakeCol" +#define MSG_MYSQL_CNC_OFF "La connexion à MySQL est fermée" +#define MSG_MYSQL_CNC_ON "La connexion à MySQL est établie" +#define MSG_MYSQL_NOT_SUP "Pas de support de MySQL dans cette version" +#define MSG_MY_CNC_ALREADY "La connexion à MySQL est déjà active" +#define MSG_NAME_CONV_ERR "Erreur de convertion du nom de noeud" +#define MSG_NAME_IS_USED "Le nom %s est déjà utilisé" +#define MSG_NCOL_GT_MAXCOL "Trop de colonnes (%d > %d max)" +#define MSG_NEW_CHAR_NULL "new char(%d) retourne Null" +#define MSG_NEW_DOC_FAILED "Impossible de créer le nouveau document" +#define MSG_NEW_RETURN_NULL "NULL renvoyé par New dans PlugEvalLike" +#define MSG_NEW_TABLE_ERR "La nouvelle table %s ne peut pas être chargée" +#define MSG_NEXT_FILE_ERROR "Erreur en recherche du fichier suivant. rc=%s" +#define MSG_NODEF_FROM_VIEW "Pas de définition de table depuis une view" +#define MSG_NODE_FOR_CHAR "Noeud %s trouvé au lieu d'un caractère" +#define MSG_NODE_SUBSET_ERR "Erreur d'initialisation de la zone Noeud %d" +#define MSG_NONCONT_EXCEPT "Exception non-continuable" +#define MSG_NON_DUP_HAVING "Clause Having dans une requête non fonctionelle" +#define MSG_NON_EVAL_SEM "Sem non évaluée: p_no=%d" +#define MSG_NOP_ZLIB_INDEX "L'indexage d'une table zlib non optimisée est impossible" +#define MSG_NOT_A_DBF_FILE "Le fichier n'a pas le format dBASE dbf " +#define MSG_NOT_ENOUGH_COLS "Pas assez de colonnes dans %s" +#define MSG_NOT_ENOUGH_MEM "Mémoire insuffisante pour cette opération" +#define MSG_NOT_FIXED_LEN "Fichier %s non fixe, len=%d lrecl=%d" +#define MSG_NOT_IMPLEMENTED "Non implementé: %.8s" +#define MSG_NOT_IMPL_JOIN "Pas implémenté pour les jointures" +#define MSG_NOT_IMPL_SET "Pas implémenté pour les opérateurs d'ensembles" +#define MSG_NOT_IMPL_YET "Pas encore implementé" +#define MSG_NOT_LINEARIZED "Arborescence des tables non linéarisée" +#define MSG_NOT_MODIFIABLE " (non modifiable)" +#define MSG_NO_0DH_HEAD "0DH manquant en fin d'en-tête (dbc=%d)" +#define MSG_NO_ACTIVE_APPL "Pas d'application active" +#define MSG_NO_ACTIVE_DB "Pas de base de données active" +#define MSG_NO_ACTIVE_UDIC "Pas de dictionaire utilisateur actif" +#define MSG_NO_AGGR_FUNC "Fonction aggrégée %d illégale à cet endroit" +#define MSG_NO_AREA_FILE "Fichier Area introuvable" +#define MSG_NO_AVAIL_RESULT "Pas de résultat disponible" +#define MSG_NO_BIG_DELETE "Délétion Partielle non implémentée pour les fichiers HUGE" +#define MSG_NO_CHAR_FROM "Conversion de type %d en caractères impossible" +#define MSG_NO_CLUSTER_COL "Pas de colonne optimisable" +#define MSG_NO_COL_ADDING "Ajouter des colonnes dans une définition existante est impossible" +#define MSG_NO_COL_DEF_AS "La définitions des colonnes est incompatible avec AS Select" +#define MSG_NO_COL_FOUND "La section colonne %s est vide" +#define MSG_NO_COL_IN_TABLE "La colonne %d n'est pas dans la table %s" +#define MSG_NO_COL_SECTION "Section colonne manquante pour la table %s" +#define MSG_NO_CONNECT_ADDR "Adresse de connection non spécifiée" +#define MSG_NO_CONST_FILTER "Filtres constants non implementés" +#define MSG_NO_CURLY_BRKT "Pas d'accolade de fermeture" +#define MSG_NO_DATABASE "Base de données %s introuvable" +#define MSG_NO_DATE_FMT "Pas de format date pour le valblock de type %d" +#define MSG_NO_DBF_INSERT "Insert pas encore implémenté pour les fichier DBF" +#define MSG_NO_DEF_FNCCOL "Colonne fonction par défaut introuvable" +#define MSG_NO_DEF_PIVOTCOL "Colonne pivot par défaut introuvable" +#define MSG_NO_DIR_INDX_RD "Pas d'accès directe des tables %s" +#define MSG_NO_DMY_DIR_ACC "Pas d'accès direct aux tables virtuelles DUMMY" +#define MSG_NO_DOM_DELETE "Délétion Partielle non implémentée pour les domaines" +#define MSG_NO_DOM_MATCH "Chaîne %.8s... non touvée dans le domaine %s" +#define MSG_NO_EDITED_LANG "Coparm: Pas de langage en édition" +#define MSG_NO_EXP_LINK "Liaison par expression invalide pour une table JCT" +#define MSG_NO_EXT_FILTER "Le filtrage ne peut se référer à une autre table" +#define MSG_NO_EXT_UPDATE "Pas de mise à jour en référence à une autre table" +#define MSG_NO_FEAT_SUPPORT "%s non supporté dans cette version" +#define MSG_NO_FILE_LIST "La table %s n'a pas de liste de fichiers" +#define MSG_NO_FLD_FORMAT "Format absent pour le champs %d de %s" +#define MSG_NO_FORMAT_COL "Type COLUMN informattable" +#define MSG_NO_FORMAT_TYPE "Le format ne peut pas être défini à partir du type %d" +#define MSG_NO_FULL_JOIN "Jointures autorisées seulement à égalité sur clé(s)" +#define MSG_NO_FUL_OUT_JOIN "Jointures externes complètes non supportées" +#define MSG_NO_FUNC_ORDER "Tri non supporté sur élément fonctionnel" +#define MSG_NO_HEAD_JOIN "Jointure sur une table non en tête" +#define MSG_NO_HQL_CONV "Conversion en HQL non disponible" +#define MSG_NO_INDEX "La table %s n'a pas d'index" +#define MSG_NO_INDEX_GBX "Pas ou mauvais index pour SQLGBX" +#define MSG_NO_INDEX_IN "Pas d'index dans %s" +#define MSG_NO_INDEX_READ "Pas d'accès directe des tables multiples" +#define MSG_NO_INIT_LANG "Pas de langage initial" +#define MSG_NO_JOIN_TO_EXP "Jointure vers une expression impossible" +#define MSG_NO_JOIN_UPDEL "Pas de jointure avec Update/Delete" +#define MSG_NO_KEY_COL "Pas de colonne clé trouvée" +#define MSG_NO_KEY_UPDATE "Le nom des clés ne peut pas être modifié" +#define MSG_NO_LANGUAGE "Pas de langage opérationnel\n" +#define MSG_NO_LANG_TO_QUIT "Pas de langage à quitter" +#define MSG_NO_LISTVAL_HERE "LSTBLK: Liste de valeurs utilisée hors contexte" +#define MSG_NO_MAP_INSERT "MAP incompatible avec Insert" +#define MSG_NO_MATCHING_COL "Pas de colonne correspondant à %s dans %s" +#define MSG_NO_MATCH_COL "Colonne correspondante introuvable" +#define MSG_NO_MEMORY "Mémoire pleine" +#define MSG_NO_MEM_CORR_SUB "Subquery corrélée en mémoire non encore implémentée" +#define MSG_NO_MODE_PADDED "Mode non supporté pour les fichiers 'padded'" +#define MSG_NO_MORE_COL "La colonne %s n'est plus dans la table pivot" +#define MSG_NO_MORE_LANG "Plus de langage, exit de %s\n" +#define MSG_NO_MORE_VAR "Les fichiers VAR ne sont plus supportés" +#define MSG_NO_MULCOL_JOIN "Jointure vers un index multi-colonne pas encore possible" +#define MSG_NO_MULT_HAVING "Clauses Having multiples non implémentées" +#define MSG_NO_MUL_DIR_ACC "Accès direct des tables multiples pas encore implémenté" +#define MSG_NO_MUL_VCT "Les tables VCT ne peuvent pas être multiples" +#define MSG_NO_MYSQL_CONN "Aucune connexion MySQL ouverte" +#define MSG_NO_MYSQL_DELETE "Pas de Delete pour les tables MySQL" +#define MSG_NO_NBCOL "Pas de NBcol" +#define MSG_NO_NBLIN "Pas de NBlin, MaxSize ou Continued" +#define MSG_NO_NBLIN_CONT "Fetch: Pas de NBlin ou Continued" +#define MSG_NO_NULL_CONST "Les constantes <null> ne sont pas prises en charge" +#define MSG_NO_ODBC_COL "Colonnes ODBC automatiques non supportées par cette version" +#define MSG_NO_ODBC_DELETE "Delete ne devrait pas être appelé pour les tables ODBC" +#define MSG_NO_ODBC_DIRECT "Accès directe des tables ODBC non encore implémenté" +#define MSG_NO_ODBC_MUL "Multiple(2) non supporté pour les tables ODBC" +#define MSG_NO_ODBC_SPECOL "Pas de colonne spéciale ODBC" +#define MSG_NO_OPT_COLUMN "Pas optimisable ou pas de colonne optimisées" +#define MSG_NO_OP_MODIF "Les modificateurs ne s'appliquent pas à %s" +#define MSG_NO_PARAMETER "Pas de paramètre" +#define MSG_NO_PART_DEL "Delete partiel des fichier %s impossible" +#define MSG_NO_PART_MAP "Mapping partiel non implémenté pour cet OS" +#define MSG_NO_PAR_BLK_INS "Insertion de bloc partiel impossible" +#define MSG_NO_PIV_DIR_ACC "Pas d'accès directe aux tables PIVOT" +#define MSG_NO_POS_ADDED "Pos_code non ajouté" +#define MSG_NO_PROMPTING "Relance impossible pour les tables distribuées" +#define MSG_NO_QRY_DELETE "Delete n'est pas utilisable pour les views QRY" +#define MSG_NO_QUERY_ARRAY "Tableaux avec QUERY non encore implémentés" +#define MSG_NO_RCUR_DSK_YET "Usage recursif de DISK non encore implementé" +#define MSG_NO_READ_32 "Lecture de 32 octets impossible" +#define MSG_NO_RECOV_SPACE "Espace non recouvrable dans le fichier index" +#define MSG_NO_REF_DELETE "Pas de suppression en référence à une autre table" +#define MSG_NO_REF_UPDATE "Pas de mise à jour en référence à une autre table" +#define MSG_NO_REMOTE_FNC "Certaines fonctions ne peuvent pas être exécutées à distance" +#define MSG_NO_ROWID_FOR_AM "Accès direct impossible de ROWID pour les tables de type %s" +#define MSG_NO_ROW_NODE "Le nom du Rownode n'est pas défini" +#define MSG_NO_SECTION_NAME "Nom de section manquant" +#define MSG_NO_SEC_UPDATE "Les noms de section ne peuvent pas être modifiés" +#define MSG_NO_SELECTED_DB "Aucune base de données sélectée" +#define MSG_NO_SELF_PIVOT "Une table ne peut se pivoter elle-même !" +#define MSG_NO_SERVER_FOUND "Serveur introuvable" +#define MSG_NO_SETPOS_YET "SetPos pas encore implémenté pour les fichier %s" +#define MSG_NO_SFEXIT_UNIX "Fonction %s non disponible sur Unix" +#define MSG_NO_SOURCE " (pas de source)" +#define MSG_NO_SPEC_COL "Pas de colonne spéciales MYSQL" +#define MSG_NO_SQL_DELETE "Delete n'est pas utilisable actuellement pour les views SQL" +#define MSG_NO_SUB_VAL "Pas de sous-value d'un tableau de type %d" +#define MSG_NO_SUCH_INDEX "La table %s n'a pas l'index %s" +#define MSG_NO_SUCH_SERVER "Serveur %s introuvable" +#define MSG_NO_SUCH_TABLE "Table %s pas dans la base de données" +#define MSG_NO_TABCOL_DATA "Pas de données pour la table %s colonne %s" +#define MSG_NO_TABLE_COL "Aucune colonne trouvée pour %s" +#define MSG_NO_TABLE_DEL "Delete non autorisé pour les tables %s " +#define MSG_NO_TABLE_DESC "Pas de bloc descriptif de table" +#define MSG_NO_TABLE_INDEX "La table %s n'a pas d'index" +#define MSG_NO_TABLE_LIST "Pas de liste de tables" +#define MSG_NO_TAB_DATA "Pas de données pour la table %s" +#define MSG_NO_TERM_IN_TOK "Les non-terminaux ne sont pas utilisables dans les règles de Token" +#define MSG_NO_TOKEN_DB "DB introuvable pour la colonne TOKEN %s" +#define MSG_NO_UNIX_CATINFO "Pas d'info catalogue sous Unix" +#define MSG_NO_UPDEL_JOIN "Pas de jointure de tables ODBC pour Update/Delete" +#define MSG_NO_VCT_DELETE "Délétion Partielle non implémentée pour les fichiers VCT" +#define MSG_NO_VIEW_COLDEF "Colonne définition impossible pour les views" +#define MSG_NO_VIEW_SORT "La View fonctionnelle %s ne peut pas être triée ou jointe" +#define MSG_NO_ZIP_DELETE "Delete sur fichier Zip non encore implementé" +#define MSG_NO_ZIP_DIR_ACC "Accès directe des tables ZDOS non encore implementé" +#define MSG_NULL_COL_VALUE "La colonne n'a pas de valeur" +#define MSG_NULL_ENTRY "InitLang, entrée nulle %d %s" +#define MSG_NULL_QUERY "Requête vide" +#define MSG_NUMVAL_NOMATCH "Disparité de Numval pour %s" +#define MSG_N_FULL_PARSES "%d significations" +#define MSG_ODBC_READ_ONLY "ODBC est actuellement en lecture seulement" +#define MSG_OFFSET_NOT_SUPP "Offset non supporté pour ce type de sous requête" +#define MSG_ONE_LANG_YET "Un langage est déjà en édition" +#define MSG_ONE_PARAM_ONLY "Un seul paramètre autorisé" +#define MSG_ONLY_LOG10_IMPL "Seul Log10 est implementé" +#define MSG_ON_LANGUAGE "Langage %.8s version %d niveau %d éditable" +#define MSG_OPENING "Ouverture" +#define MSG_OPENING_QUERY "Ouverture de la requête" +#define MSG_OPEN_EMPTY_FILE "Ouverture du fichier vide %s: %s" +#define MSG_OPEN_ERROR "Erreur d'ouverture %d en mode %d sur %s: " +#define MSG_OPEN_ERROR_IS "Erreur à l'ouverture de %s: %s" +#define MSG_OPEN_ERROR_ON "Erreur d'ouverture sur %s" +#define MSG_OPEN_MODE_ERROR "Erreur d'ouverture(%s) %d sur %s" +#define MSG_OPEN_SORT_ERROR "Erreur logique de tri dans QUERY Open" +#define MSG_OPEN_STRERROR "Erreur à l'ouverture: %s" +#define MSG_OPEN_W_ERROR "Erreur à l'ouverture de %s en écriture" +#define MSG_OPTBLK_RD_ERR "Erreur à la lecture d'un bloc optimisation: %s" +#define MSG_OPTBLK_WR_ERR "Erreur à l'écriture d'un bloc optimisation: %s" +#define MSG_OPTIMIZING "Optimisation de " +#define MSG_OPT_BMAP_RD_ERR "Erreur en lecture des bitmaps d'optimisation: %s" +#define MSG_OPT_BMAP_WR_ERR "Erreur en écriture des bitmaps d'optimisation: %s" +#define MSG_OPT_CANCELLED "Optimisation interrompue par l'utilisateur" +#define MSG_OPT_DVAL_RD_ERR "Erreur en lecture des valeurs distinctes: %s" +#define MSG_OPT_DVAL_WR_ERR "Erreur en écriture des valeurs distinctes: %s" +#define MSG_OPT_HEAD_RD_ERR "Erreur en lecture de l'entête du fichier opt: %s" +#define MSG_OPT_HEAD_WR_ERR "Erreur en écriture de l'entête du fichier opt: %s" +#define MSG_OPT_INIT "Optimisation initialisée" +#define MSG_OPT_LOGIC_ERR "Erreur logique dans SetBitmap, i=%d" +#define MSG_OPT_MAX_RD_ERR "Erreur en lecture des valeurs maxi: %s" +#define MSG_OPT_MAX_WR_ERR "Erreur en écriture des valeurs maxi: %s" +#define MSG_OPT_MIN_RD_ERR "Erreur en lecture des valeurs mini: %s" +#define MSG_OPT_MIN_WR_ERR "Erreur en écriture des valeurs mini: %s" +#define MSG_OPT_NOT_MATCH "Le fichier opt %s n'est pas à jour" +#define MSG_OP_RES_TOO_LONG "Résultat trop long pour l'opérateur=%d" +#define MSG_ORDER_OUT_RANGE "Tri: Order %d hors limite" +#define MSG_ORDER_TWICE "Un même élément est trié deux fois" +#define MSG_PAGE_ERROR "Erreur de pagination" +#define MSG_PARM_CNT_MISS "Disparité du nombre de Paramètres" +#define MSG_PARSE_NULL_SEM "Sémantique nulle" +#define MSG_PARSING_QUERY "Analyse de la requête" +#define MSG_PIX_ERROR "Pix %s erreur règle no=%u\n" +#define MSG_PIX_TEST_ERROR "Règle=%u: pix-TEST pas dans le premier noeud\n" +#define MSG_PLG_READ_ONLY "PLG est actuellement en lecture seulement" +#define MSG_PLM_NULL_SFP "TABPLM ReadDB: Sfp est NULL" +#define MSG_PLUG_NOT_INIT "Plug n'est pas initialisé\n" +#define MSG_PLUG_NOT_RUN "Plug n'est pas en marche" +#define MSG_PNODE_RULE "(Noeud %d règle %d) " +#define MSG_POS_TOO_LONG "%s trop long (>%d)" +#define MSG_PREC_VBLP_NULL "ARRAY SetPrecision: Vblp est NULL" +#define MSG_PRIV_INSTR "Instruction privilégiée" +#define MSG_PROCADD_ERROR "Erreur %d sur l'adresse de %s" +#define MSG_PROCESS_SUBQRY "Sub-Query en cours de traitement" +#define MSG_PROC_WOULD_LOOP "Bouclage du traitement (maxres=%d maxlin=%d)" +#define MSG_PROGRESS_INFO "Informations sur le traitement en cours" +#define MSG_PROMPT_CANCEL "Relance annulée" +#define MSG_PROMPT_NIY "Prompt non implémenté pour cette configuration" +#define MSG_PTR_NOT_FOUND "Pointeur introuvable Num=%d ti1=%d" +#define MSG_PXDEF_IS_NULL "Pxdef est NULL" +#define MSG_QRY_READ_ONLY "Les views QRY sont en lecture seulement" +#define MSG_QUERY_CANCELLED "Requête interrompue par l'utilisateur" +#define MSG_QUERY_NOT_EXEC "Requête non exécutée" +#define MSG_QUERY_SAVED "Requête %s sauvegardée" +#define MSG_QUOTE_IN_QUOTE "Appostrophe dans un champ entre appostrophe ligne %d" +#define MSG_RANGE_NIY "Range pas encore implémenté pour %s" +#define MSG_RANGE_NO_JOIN "Range non compatible avec les index de jointure" +#define MSG_RC_READING "rc=%d en lecture de la table %s" +#define MSG_READB_BAD_INIT "%s ReadDB appelé avec Init=0" +#define MSG_READCOL_ERROR "SQLCOL: erreur dans ReadColumn" +#define MSG_READING "Lecture" +#define MSG_READING_FROM "Lecture de %s" +#define MSG_READING_RECORD "Erreur en lecture de l'enregistrement %d de %s" +#define MSG_READY "Prêt" +#define MSG_READ_ERROR "Erreur en lecture sur %s: %s" +#define MSG_READ_ERROR_RC "Erreur en lecture, rc=%d" +#define MSG_READ_MEM_ERROR "Lecture mémoire %d: taille=%d" +#define MSG_READ_ONLY "Cette table protégée en lecture seule ne peut être modifiée" +#define MSG_READ_SEEK_ERROR "Erreur de recherche en lecture: %s" +#define MSG_READ_SEG_ERROR "Lecture segment %d: taille=%d" +#define MSG_RECEIVED "Reçu %c\n" +#define MSG_RECORD_ERROR "Erreur à la lecture de l'enregistrement %d de %s" +#define MSG_RECORD_NO_SEP "Enregistrement sans séparateur" +#define MSG_REC_SKIPPED " (%d lignes erronnées sautées par l'option MaxErr)" +#define MSG_REDUCE_INDEX "Réduction de l'index" +#define MSG_REGISTER_ERR "Enregistrement NS impossible, préfix='%s' et href='%s'" +#define MSG_REMOTE_CONN_ERR "La connection éloignée a échoué" +#define MSG_REMOVE_ERROR "Erreur en supprimant %s: %s" +#define MSG_REMOVE_NOT_IMPL "Remove non implémenté pour TDB non Table" +#define MSG_RENAME_ERROR "Erreur renommant %s en %s: %s" +#define MSG_RENUM_RULES "Renumérotez les règles et réentrez ADD (règle sauvegardée dans la zone tampon)" +#define MSG_REORDER_INDEX "Reclassement de l'index" +#define MSG_REQU_ARG_NUM "La fonction %s doit avoir %d arguments" +#define MSG_RESET_TO "%s remis à %d" +#define MSG_RES_NOT_UNIQUE "Le résultat n'est pas unique" +#define MSG_RET_FROM_LANG "Retour au language %s version %d.%d du language %s version %d.%d" +#define MSG_ROWID_NOT_IMPL "RowNumber non implémenté pour les tables de type %s" +#define MSG_ROWS_SELECTED "%d lignes sélectionnées en %.2lf sec" +#define MSG_ROWS_TRUNCATED " (tronqué par MAXRES, LIMIT, FREQ ou AreaSize)" +#define MSG_ROW_ARGNB_ERR "ROW: disparité du nombre d'arguments (%d,%d)" +#define MSG_RPC_SERVER_ERR "Erreur logique dans TABMUL::MakeCol" +#define MSG_RSC_ALLOC_ERROR "Erreur d'allocation mémoire dans Rescol %s" +#define MSG_RULE_ENTERED "Règle %d entrée" +#define MSG_RULE_SUBSET_ERR "Erreur d'initialisation de la zone Règles" +#define MSG_SAVING_INDEX "Sauvegarde du fichier index" +#define MSG_SCAN_NOT_IMP "Scan non implémenté" +#define MSG_SEC_KEY_FIRST "Les sections et clés doivent être insérées en premier" +#define MSG_SEC_NAME_FIRST "Le nom de section doit être en tête de liste en insertion" +#define MSG_SEC_NOT_FOUND "Section %s absente de %s" +#define MSG_SEEK_ERROR "Seek erreur dans CopyHeader" +#define MSG_SEMANTIC_TREE "Arbre sémantique" +#define MSG_SEM_BAD_REF "Sem @%d référence un argument de type non 0 ou 1" +#define MSG_SEM_UNKNOWN "inconnue, rc=%d" +#define MSG_SEP_IN_FIELD "Le champ %d contient le caractère séparateur" +#define MSG_SEQUENCE_ERROR "HSTMT: Allocation hors séquence" +#define MSG_SETEOF_ERROR "Erreur %d dans SetEndOfFile" +#define MSG_SETRECPOS_NIY "SetRecpos non implémenté pour ce type de table" +#define MSG_SET_LOCALE "Locale fixée à %s" +#define MSG_SET_NULL_DOM "Valeur %d donnée à un domaine nul" +#define MSG_SET_OP_NOT_IMPL "Opérateurs ensemblistes non implementés" +#define MSG_SET_STR_TRUNC "SetValue: Chaîne de caractères tronquée" +#define MSG_SEVERAL_TREES "Jointure non spécifiée pour certaines tables" +#define MSG_SFP_ERROR "Erreur sur SetFilePointer: %s" +#define MSG_SFUNC_NOT_IMPL "Fonction scalaire %s non implémentée" +#define MSG_SHARED_LIB_ERR "Erreur au chargement de la librairie partagée %s: %s" +#define MSG_SINGLE_STEP "Pas à pas" +#define MSG_SLEEP "J'ai dormi %d milliseconds" +#define MSG_SMART_SORTING "Récupération des lignes triées (passage %d de %d)" +#define MSG_SMART_SORT_ERR "Erreur logique 1 dans Smart Sort" +#define MSG_SORTING "Tri en cours" +#define MSG_SORTING_INDEX "Tri de l'index" +#define MSG_SORTING_VAL "Tri de %d valeurs" +#define MSG_SORT_JOIN_INDEX "Tri de l'index de jointure" +#define MSG_SPCOL_READONLY "La colonne spéciale %s est en lecture seulement" +#define MSG_SPEC_CMD_SEP "Les commandes spéciales doivent être exécutées séparément" +#define MSG_SQL_BAD_TYPE "RephraseSQL: type %d non supporté" +#define MSG_SQL_BLOCK_MISM "CheckColumn: bloc SQL courant non correspondant" +#define MSG_SQL_CONF_ERROR "Erreur SQL: SQL_CONFORMANCE" +#define MSG_SQL_READ_ONLY "Les views SQL sont actuellement en lecture seulement" +#define MSG_SRCH_CLOSE_ERR "Erreur à la fermeture de l'Handle de recherche" +#define MSG_SRC_TABLE_UNDEF "La table source n'est pas définie" +#define MSG_STACK_ERROR "Erreur sur la pile, i=%d\n" +#define MSG_STACK_OVERFLOW "Parser: Débordement de la pile\n" +#define MSG_STRG_NOT_FOUND "Chaîne introuvable" +#define MSG_STRING_INV_LIST "Liste invalide pour SemString" +#define MSG_STRING_TOO_BIG "Chaîne trop grande pour le domaine %s" +#define MSG_SUBALLOC_ERROR "Pas assez de mémoire en zone %p pour allouer %d (utilisé=%d libre=%d)" +#define MSG_SUBAL_HUGE_ERR "Pas assez de mémoire en zone huge %p pour allouer %d" +#define MSG_SUBARG_NOSEM "Argument @ ou sous-phrase de niveau %d pointe sur un noeud sans Sem" +#define MSG_SUBARG_OUTRANGE "Argument @ ou sous-phrase de niveau %d hors limite" +#define MSG_SUBQRY_ONEITEM "Une Sub-Query ne doit avoir qu'une sélection" +#define MSG_SUBSET_ERROR "SubSet erreur dans LoadDB" +#define MSG_SUB_OPEN_YET "Subquery déjà ouverte" +#define MSG_SUB_RES_TOO_LNG "Résultat trop long pour SUBSTR" +#define MSG_SYNTAX_ERROR "Erreur de syntaxe" +#define MSG_SYSTEM_ERROR "Erreur système %d" +#define MSG_S_ACCESS_DENIED "%s: accès non autorisé" +#define MSG_S_ERROR "%s erreur" +#define MSG_S_ERROR_NUM "%s: erreur=%d" +#define MSG_S_INTRUPT_ERROR "%s: erreur interruption" +#define MSG_S_INVALID_PARM "%s: paramètre invalide" +#define MSG_S_INV_ADDRESS "%s: adresse invalide" +#define MSG_S_UNKNOWN_ERROR "%s: erreur de code %u inconnu" +#define MSG_TABDIR_READONLY "Les tables DIR sont en lecture seulement" +#define MSG_TABLE_ALREADY "La table %s existe déjà" +#define MSG_TABLE_ALTERED "Table %s %s altérée" +#define MSG_TABLE_CREATED "%s table %s créée" +#define MSG_TABLE_DROPPED "Table %s supprimée" +#define MSG_TABLE_MULT_JOIN "Utilisation multiple de la table %s pour jointure" +#define MSG_TABLE_NOT_IN_DB "La table %s n'existe pas dans %s" +#define MSG_TABLE_NOT_OPT "Table non optimisable" +#define MSG_TABLE_NO_INDEX "La table %s n'est pas indexable" +#define MSG_TABLE_NO_OPT "La table %s n'existe pas ou de type non optimisable" +#define MSG_TABLE_READ_ONLY "Les tables %s sont en lecture seulement " +#define MSG_TABMUL_READONLY "Les tables multiples sont en lecture seulement" +#define MSG_TAB_NOT_LOADED " (certaines tables n'ont put être chargées)" +#define MSG_TAB_NOT_SPEC "Table non specifiée" +#define MSG_TB_VW_NOTIN_DB "Table ou view %s pas dans la base de données" +#define MSG_TDB_NXT_NOT_NUL "Tdb.Next non NULL" +#define MSG_TDB_USE_ERROR "Erreur, Tdbp->Use=%d" +#define MSG_TOO_MANY_COLS "Trop de colonnes" +#define MSG_TOO_MANY_COLTAB "Trop de colonnes dans %s (%d)" +#define MSG_TOO_MANY_FIELDS "Trop de champs ligne %d de %s" +#define MSG_TOO_MANY_JUMPS "Trop de niveaux de saut" +#define MSG_TOO_MANY_KEYS "Trop de clés (%d)" +#define MSG_TOO_MANY_POS "Trop de pos_codes" +#define MSG_TOO_MANY_TABLES "Trop de tables (%d)" +#define MSG_TOPSEM_ERROR "Erreur inconnue dans TopSem" +#define MSG_TO_BLK_IS_NULL "To Blk est nul" +#define MSG_TO_FTR_NOT_NULL "Set.To_Ftr n'est pas nul" +#define MSG_TO_PIX_NOT_NULL "Set.To_Pix n'est pas nul" +#define MSG_TO_SEM_NOT_NULL "Set.To_Sem n'est pas nul" +#define MSG_TRUNCATE_ERROR "Erreur en troncation: %s" +#define MSG_TRUNC_BY_ESTIM "Tronqué par l'option Estimate" +#define MSG_TYPES_ERROR "Erreur sur Types(%d)" +#define MSG_TYPE_CONV_ERROR "Type non convertible dans une expression" +#define MSG_TYPE_DEF_MISM "Disparité entre type et définition" +#define MSG_TYPE_MISMATCH "Clé et source ne sont pas du même type" +#define MSG_TYPE_RECFM_MISM "Disparité entre Type et Recfm" +#define MSG_TYPE_TO_VERIFY "Type à vérifier: %d" +#define MSG_TYPE_VALUE_ERR "Colonne %s: disparité type(%s)/valeur(%s)" +#define MSG_UNBALANCE_QUOTE "Appostrophe en trop ligne %d" +#define MSG_UNDEFINED_AM "COLBLK %s: méthode d'accès indéfinie" +#define MSG_UNDEFINED_PATH "Chemin d'accès indéfini pour Plgcnx.ini" +#define MSG_UNDEF_COL_COUNT "Count sur colonne non définie" +#define MSG_UNKNOWN_DOMAIN "Domaine inconnu %s" +#define MSG_UNKNOWN_ERROR "Erreur inconnue" +#define MSG_UNKNOWN_EXCPT "Exception non répertoriée" +#define MSG_UNKNOWN_NAME "Nom inconnu: %.8s" +#define MSG_UNKNOWN_PATH "Chemin d'accès inconnu pour Plgcnx.ini" +#define MSG_UNKNOWN_POS "Nom pos_code inconnu: %s" +#define MSG_UNKNOWN_SEM "Sem %.8s inconnue, rc=%d" +#define MSG_UNKNOWN_SYNONYM "Synonyme inconnu" +#define MSG_UNKNW_QRY_TYPE "ReadDB: type de requête inconnu" +#define MSG_UNKN_ERR_CODE "Erreur de code %d inconnu" +#define MSG_UNLOADABLE " inchargeable: " +#define MSG_UNLOADABLE_PRM "%s inchargeable: %s" +#define MSG_UNMATCH_FIL_ARG "Argument de filtre dépareillé" +#define MSG_UNQ_COL_SEV_TAB "La colonne %s non qualifiée est dans plusieurs tables" +#define MSG_UNRESOLVED_ARG "?Argument manquant: %s non résolu en %d ligne %d" +#define MSG_UPDATE_ERROR "Erreur en Update sur %s" +#define MSG_UPDATING_ROWS "Mise à jour des lignes" +#define MSG_UPD_ZIP_NOT_IMP "Mise à jour des tables ZDOS non encore implementé" +#define MSG_UP_LANGUAGE "Bloc langage %.8s version %d niveau %d chargé" +#define MSG_USED_FREE_MEM "Sarea: utilisé %d, libre %d" +#define MSG_USETEMP_IS "Usetemp est : %s" +#define MSG_USETEMP_RESET ". Usetemp remis à Auto" +#define MSG_USETEMP_SET "Usetemp fixé à %s" +#define MSG_USE_NO_MATCH "Use non correspondant : Use=%d, ti2=%d, ti3=%d" +#define MSG_USING_INDEX " (Indexé par" +#define MSG_VALIST_MISMATCH "Disparité des listes de valeurs" +#define MSG_VALSTR_TOO_LONG "Valeur %s trop longue pour une chaîne de longueur %d" +#define MSG_VALTYPE_NOMATCH "Disparité types de valeur" +#define MSG_VALUE_ERROR "Colonne %s: bloc valeur nul" +#define MSG_VALUE_NOT_ALLOC "Valeur non allouée pour la colonne R%d %s" +#define MSG_VALUE_TOO_BIG "Valeur %lld trop grande pour la colonne %s" +#define MSG_VALUE_TOO_LONG "Valeur %s trop longue pour la colonne %s de longueur %d" +#define MSG_VAL_ALLOC_ERR "Allocation impossible du noeud valeur" +#define MSG_VAL_TOO_LONG "Valeur %s trop longue pour le champ %s" +#define MSG_VIEW_ALREADY "La VIEW %s existe déjà" +#define MSG_VIEW_CREATED "%s view %s créée" +#define MSG_VIEW_DROPPED "View %s supprimée" +#define MSG_VIEW_NOT_IN_DB "%s n'est pas une View de %s" +#define MSG_VIR_NO_DELETE "Delete impossible sur les tables %s" +#define MSG_VIR_READ_ONLY "Les tables virtuelles %s sont en lecture seulement" +#define MSG_VM_LANG "Langage au format VM, non supporté" +#define MSG_VOID_FIRST_ARG "Le premier argument ne doit pas être vide" +#define MSG_VOID_IN_STRING "Erreur: chaîne IN vide" +#define MSG_VOID_ORDER_LIST "Liste de tri vide, erreur système ?" +#define MSG_VOID_POS_DICT "Dictionnaire interne du langage vide" +#define MSG_VOID_QUERY "Requête vide %s" +#define MSG_WORK_AREA "Espace de travail: %s" +#define MSG_WORK_TOO_SMALL "Zone de travail trop petite, accroître AreaSize" +#define MSG_WRITE_ERROR "Erreur à l'écriture de %s" +#define MSG_WRITE_SEEK_ERR "Erreur de recherche en écriture: %s" +#define MSG_WRITE_STRERROR "Erreur en écriture sur %s: %s" +#define MSG_WRITING "Ecriture" +#define MSG_WRITING_ERROR "Erreur à l'écriture de %s: %s" +#define MSG_WRITING_QUERY "Erreur à l'écriture de la requête: " +#define MSG_WRONG_ARG_NUM "La fonction %s ne prend pas %d arguments" +#define MSG_WRONG_COL_NUM "Numéro de colonne %d trop grand pour %s" +#define MSG_WRONG_DB_LIST "Liste des bases de données incorrecte ou vide" +#define MSG_WRONG_FUNCTION "Mauvaise fonction %d" +#define MSG_WRONG_OP_PARM "Mauvais opérateur ou paramètres pour %s" +#define MSG_WRONG_PARMS "Mauvais paramètres pour %s" +#define MSG_WRONG_PASSWORD "Mot de passe illégal pour %s" +#define MSG_WRONG_TYPE "type non supporté" +#define MSG_WRONG_USERFILE "La Userfile a une mauvaise taille %d" +#define MSG_WS_CONV_ERR "Erreur de convertion de %s en WS" +#define MSG_XCOL_MISMATCH "La colonne %s ne correspond pas à l'index" +#define MSG_XDB_DEL_ERROR "Erreur en supprimant des entrées du fichier XDB" +#define MSG_XFILE_READERR "Erreur %d en lisant le fichier index" +#define MSG_XFILE_TOO_SMALL "Le fichier index est plus petit que la taille de l'index" +#define MSG_XFILE_WRITERR "Erreur en écrivant le fichier index: %s" +#define MSG_XMLTAB_INIT_ERR "Erreur d'initialisation de la table XML" +#define MSG_XML_INIT_ERROR "Erreur d'initialisation du nouveau fichier XML" +#define MSG_XPATH_CNTX_ERR "Le nouveau contexte XPath ne peut être créé" +#define MSG_XPATH_EVAL_ERR "Impossible d'évaluer l'emplacement xpath '%s'" +#define MSG_XPATH_NOT_SUPP "Xpath non supporté colonne %s" +#define MSG_X_ARG_ADDED "%d arguments ajoutés" +#define MSG_X_ARG_SET "%d arguments ont été initialisés" +#define MSG_X_ON_TAB " %s sur %s(" +#define MSG_ZERO_DIVIDE "Division par zéro dans une expression" diff --git a/storage/connect/frmsg2.h b/storage/connect/frmsg2.h new file mode 100644 index 00000000000..487db3395fb --- /dev/null +++ b/storage/connect/frmsg2.h @@ -0,0 +1,1013 @@ +#define MSG_ACCESS_VIOLATN "Violation accŠs m‚moire" +#define MSG_ACT_ALLOC_FAIL "PlugInitLang: Erreur d'allocation du bloc Activity" +#define MSG_ADDVAL_ERROR "Erreur %d dans AddValue" +#define MSG_ADD_BAD_TYPE "Ajout d'une valeur de type %s non conforme dans un tableau %s" +#define MSG_ADD_NULL_DOM "Ajout de la chaŒne %s … un domaine nul" +#define MSG_ADPOS_IN_DICTP "ADPOS au travail dans User_Dictp" +#define MSG_AFTER " aprŠs: " +#define MSG_ALG_CHOICE_AUTO "Le choix du meilleur algorithme est automatique" +#define MSG_ALG_CHOICE_BAD "Choix d'algorithme invalide, remis … AUTO" +#define MSG_ALG_CHOICE_QRY "Utilise l'algorithme 'Query'" +#define MSG_ALG_CURLY_BRK "Le choix de l'algorithme d‚pend des accolades externes" +#define MSG_ALLOC_ERROR "Erreur d'allocation de %s" +#define MSG_ALL_DELETED "Toutes les lignes enlev‚es en %.2lf sec" +#define MSG_ALTER_DB_ERR "Impossible de d‚terminer la base de donn‚es … modifier" +#define MSG_AMBIG_COL_QUAL "Qualificateur ambigu %s pour la colonne %s" +#define MSG_AMBIG_CORREL "Select %s.* corr‚lation ambigue" +#define MSG_AMBIG_SPEC_COL "Colonne sp‚ciale ambigue %s" +#define MSG_ANSWER_TYPE "R‚ponse de type" +#define MSG_API_CONF_ERROR "Erreur SQL: API_CONFORMANCE" +#define MSG_APPL_ACCESSIBLE "Application %s accessible" +#define MSG_APPL_ACTIVE "Application %s encore active" +#define MSG_APPL_BAD_SAVE "Application %s partiellement sauvegard‚e" +#define MSG_APPL_CREATED "Application %s cr‚‚" +#define MSG_APPL_IS_ACTIVE "Application d‚j… active" +#define MSG_APPL_NOT_INIT "Application non initialis‚e" +#define MSG_APPL_NOT_LOADED "Application non charg‚e" +#define MSG_APPL_QUIT "Fin de l'application %s" +#define MSG_APPL_SAVED "Application %s sauvegard‚e" +#define MSG_APP_STILL_ACTIV "Application du langage %s encore active (non lib‚rable)" +#define MSG_AREAFILE_NOTFND "Fichier Area introuvable" +#define MSG_ARGS_SYNTAX_ERR "?SetArgs erreur de syntaxe: %s inattendu aprŠs %s" +#define MSG_ARG_ALREADY_SET "Argument %d d‚j… allou‚" +#define MSG_ARG_NOT_AN_ATTR "L'argument n'est pas un attribut (type %d erron‚)" +#define MSG_ARG_OUT_CONTEXT "Argument de type @ utilis‚ hors contexte" +#define MSG_ARG_OUT_RANGE "Argument de phrase valant %d hors limite" +#define MSG_ARG_PTR_NOSEM "Argument valant %d pointe sur un noeud sans Sem" +#define MSG_ARG_PTR_NOSEMS "Argument valant %d pointe sur un noeud sans s‚mantique" +#define MSG_ARG_REF_LOOP "?Bouclage entre r‚f‚rences crois‚es des arguments" +#define MSG_ARG_TWO_CONST "Le 2Šme argument de %s doit ˆtre constant" +#define MSG_ARRAY_ALLOC_ERR "Erreur d'allocation m‚moire dans ARRAY" +#define MSG_ARRAY_BNDS_EXCD "Hors limite de tableau" +#define MSG_ARRAY_ERROR "Erreur de fonctionnement k=%d n=%d" +#define MSG_ATTRIBUTE_ERROR "Erreur rŠgle %u attribut %s: " +#define MSG_ATT_NOT_CASE "Mauvaise valeur %d pour attribut (pas une CaseValue)" +#define MSG_ATT_POSCODE_BIG "Code attribut %d trop grand (max=%d)" +#define MSG_AVGLEN_ERROR "avglen doit ˆtre entre %d et %d" +#define MSG_BAD_AGGREG_FUNC "Fonction aggr‚g‚e %d non support‚e" +#define MSG_BAD_ARGTYPES "Argument de type invalide pour %s" +#define MSG_BAD_ARGUMENTS "Argument non attach‚s pour %s" +#define MSG_BAD_ARG_NUM "Nombre d'arguments invalide %d" +#define MSG_BAD_ARG_TYPE "Type d'argument %d invalide" +#define MSG_BAD_ARRAY_OPER "Les tableaux doivent utiliser l'op‚rateur IN" +#define MSG_BAD_ARRAY_TYPE "Type=%d invalide pour un tableau" +#define MSG_BAD_ARRAY_VAL "Les tableaux doivent avoir le mˆme nombre de valeurs" +#define MSG_BAD_BIN_FMT "Format invalide %c pour la colonne BIN %s" +#define MSG_BAD_BLK_ESTIM "Nombre de blocs sup‚rieur … l'estimation" +#define MSG_BAD_BLK_SIZE "Taille du bloc %d non conforme" +#define MSG_BAD_BYTE_NUM "Le nombre d'octets ‚crits est faux" +#define MSG_BAD_BYTE_READ "Le nombre d'octets lus est faux" +#define MSG_BAD_CARDINALITY "Appel invalide de Cardinality pour une table multiple" +#define MSG_BAD_CASE_SPEC "Min/Maj: sp‚cification %c incorrecte, recommencez: " +#define MSG_BAD_CHAR_SPEC "Sp‚cification '%s' invalide pour caractŠre" +#define MSG_BAD_CHECK_TYPE "Sous-type %d invalide pour CheckColumn" +#define MSG_BAD_CHECK_VAL "Valeur pour Check invalide '%s'" +#define MSG_BAD_COLCRT_ARG "COLCRT: Arg invalide (type=%hd, domain=%hd)" +#define MSG_BAD_COLDEF_TYPE "Coldefs: type ill‚gal %d" +#define MSG_BAD_COLIST_ITEM "El‚ment invalide dans une Colist" +#define MSG_BAD_COLIST_TYPE "Mauvais type=%d pour une Colist" +#define MSG_BAD_COLSIZE "Colsize %d trop petit pour cette base de donn‚es" +#define MSG_BAD_COL_ENTRY "Entr‚e invalide pour la colonne %s" +#define MSG_BAD_COL_FORMAT "Type de formattage %d invalide pour une colonne" +#define MSG_BAD_COL_IN_FILT "Colonne incorrecte dans un filtre" +#define MSG_BAD_COL_QUALIF "Qualificateur invalide %s pour la colonne %s" +#define MSG_BAD_COL_TYPE "Type invalide %s pour la colonne %s" +#define MSG_BAD_COL_XPATH "Xpath invalide colonne %s de la table HTML %s" +#define MSG_BAD_COMPARE_OP "Op‚rateur de comparaison %d invalide" +#define MSG_BAD_CONST_TYPE "Type=%d invalide pour une constante" +#define MSG_BAD_CONV_TYPE "Convertion de type invalide %d" +#define MSG_BAD_CORREL "Select %s.* corr‚lation absente" +#define MSG_BAD_DATETIME "Valeur date/temps invalide" +#define MSG_BAD_DATE_OPER "Op‚rateur de date inattendu %d" +#define MSG_BAD_DBF_FILE "Le fichier DBF %s est alt‚r‚" +#define MSG_BAD_DBF_REC "Fichier DBF %s alt‚r‚ enregistrement %d" +#define MSG_BAD_DBF_TYPE "Type DBF %c non support‚" +#define MSG_BAD_DEF_ARG "Argument invalide pour INDEXDEF (type=%hd, domain=%hd)" +#define MSG_BAD_DEF_READ "EOF inattendue en lecture diff‚r‚e" +#define MSG_BAD_DEF_TYPE "Type de colonne invalide" +#define MSG_BAD_DIRECTORY "R‚pertoire invalide %s: %s" +#define MSG_BAD_DIST_JN_FIL "Filtre de jointure distincte invalide" +#define MSG_BAD_DIST_JOIN "Sp‚cification invalide de jointure distincte" +#define MSG_BAD_DOM_COL_DEF "D‚finition de colonnes invalide pour un domaine" +#define MSG_BAD_DOM_VALUE "La valeur %d n'appartient pas au domaine" +#define MSG_BAD_EDIT_INIT "Coparm: ‚dition %s initialis‚e improprement" +#define MSG_BAD_EVAL_TYPE "Fonction scalaire de type=%d invalide" +#define MSG_BAD_EXEC_MODE "Mode d'ex‚cution invalide '%s'" +#define MSG_BAD_EXP_ARGTYPE "Argument de type %d invalide pour une expression" +#define MSG_BAD_EXP_OPER "Op‚rateur=%d invalide pour expression" +#define MSG_BAD_FETCH_RC "Code retour inattendu de Fetch %d" +#define MSG_BAD_FIELD_FMT "Format de champ invalide %c pour %s" +#define MSG_BAD_FIELD_RANK "Rang %d invalide pour la colonne %s" +#define MSG_BAD_FIELD_TYPE "Mauvais type de champ %s" +#define MSG_BAD_FILE_HANDLE "Handle de fichier invalide: %s" +#define MSG_BAD_FILE_LIST "La section liste de fichiers est erron‚e" +#define MSG_BAD_FILTER "Mauvais filtre: Opc=%d B_T=%d %d Type=%d %d" +#define MSG_BAD_FILTER_CONV "Conversion filtre incorrecte, B_T=%d,%d" +#define MSG_BAD_FILTER_LINK "Op‚rateur de chaŒnage ill‚gal %d" +#define MSG_BAD_FILTER_OP "Op‚rateur de filtre invalide %d" +#define MSG_BAD_FILTEST_OP "Op‚rateur invalide %d %d pour FilTest" +#define MSG_BAD_FLD_FORMAT "Format invalide pour le champs %d de %s" +#define MSG_BAD_FLD_LENGTH "Champs %s trop long (%s --> %d) ligne %d de %s" +#define MSG_BAD_FLOAT_CONV "Convertion invalide d'un tableau flottant" +#define MSG_BAD_FPARM_NEXT "Coparm: FPARM avec Next non nul" +#define MSG_BAD_FREQ_SET "Sp‚cification erronn‚e de Freq pour la colonne %s" +#define MSG_BAD_FUNC_ARG "Funcarg de type %d non impl‚ment‚" +#define MSG_BAD_FUNC_ARGTYP "Mauvais type d'argument=%d pour une fonction" +#define MSG_BAD_FUNC_MODE "%s: mode invalide %d" +#define MSG_BAD_GENRE "Genre est invalide" +#define MSG_BAD_GETVIEW_RET "GetView: type de retour %d invalide" +#define MSG_BAD_HANDLE_VAL "Valeur Handle invalide" +#define MSG_BAD_HAV_FILTER "Filtre Having sur une requˆte non group‚e" +#define MSG_BAD_HAV_FILTYPE "Filtre invalide pour clause Having" +#define MSG_BAD_HEADER "Fichier %s: bloc en-tˆte alt‚r‚" +#define MSG_BAD_HEADER_VAL "Valeur invalide pour Header" +#define MSG_BAD_HEAD_END "Lecture fin d'en-tˆte impossible" +#define MSG_BAD_INDEX_COL "Colonne %s invalide pour index %s" +#define MSG_BAD_INDEX_DEF "D‚finition invalide pour index %s" +#define MSG_BAD_INDEX_FILE "Fichier index %s corrompu" +#define MSG_BAD_INDEX_PART "D‚finition colonne invalide pour index %s" +#define MSG_BAD_INPUT "Entr‚e incorrecte" +#define MSG_BAD_IN_ARGTYPE "Argument de type invalide pour l'op‚rateur IN" +#define MSG_BAD_IN_ENDING "Erreur: fin de chaŒne IN invalide" +#define MSG_BAD_IN_STRING "La chaŒne IN commence ou finie par des caractŠres invalides %c ... %c" +#define MSG_BAD_JCOL_TYPE "Erreur logique JCT: disparit‚ des types colonnes" +#define MSG_BAD_JOIN_EXP "Expression invalide pour une jointure" +#define MSG_BAD_JOIN_FILTER "Filtre de jointure invalide" +#define MSG_BAD_JOIN_OP "Op‚rateur de joint invalide %d" +#define MSG_BAD_LANG_SIZE "Le fichier langage a une mauvaise taille %d" +#define MSG_BAD_LINEFLD_FMT "Format invalide ligne %d champs %d de %s" +#define MSG_BAD_LINE_LEN "Longueur ligne non ‚gale … Lrecl" +#define MSG_BAD_LIST_TYPE "Type de liste invalide %d" +#define MSG_BAD_LOCALE "Locale invalide %s" +#define MSG_BAD_LOCDFON_ARG "Mauvais paramŠtre pour LOCDFON" +#define MSG_BAD_LOCNODE_USE "Usage inattendu de LOCNODE" +#define MSG_BAD_LRECL "Disparit‚ lrecl table/fichier (%d,%hd)" +#define MSG_BAD_MAX_HAVING "MAXTMP trop petit pour Having" +#define MSG_BAD_MAX_NREC "MaxRec=%d ne correspond pas … MaxBlk=%d Nrec=%d" +#define MSG_BAD_MAX_PARAM "Mauvais paramŠtres pour sp‚cifier une valeur maximum" +#define MSG_BAD_MAX_SETTING "Mauvaise valeur '%c' pour max" +#define MSG_BAD_MERGE_TYPE "Le type %d ne pas ˆtre intercall‚" +#define MSG_BAD_NODE_TYPE "Type noeud erron‚ pour la table" +#define MSG_BAD_OFFSET_VAL "Nul offset invalide pour une table CSV" +#define MSG_BAD_OPEN_MODE "Mode d'ouverture invalide %d" +#define MSG_BAD_OPERATOR "Op‚rateur invalide %s" +#define MSG_BAD_ORDER_MODE "Mode de tri %c invalide" +#define MSG_BAD_ORDER_TYPE "Tri sur objet de type=%d invalide" +#define MSG_BAD_OUTER_JOIN "Jointure externe invalide sur table enfant" +#define MSG_BAD_PAD_ARGTYP "Argument de type invalide pour Pad ou Justify" +#define MSG_BAD_PARAMETERS "%.8s: Mauvais paramŠtres" +#define MSG_BAD_PARAM_TYPE "%.8s: ParamŠtre de type=%d invalide" +#define MSG_BAD_PARM_COUNT "Nombre de paramŠtres incoh‚rent" +#define MSG_BAD_PHASE_NUM "Num‚ro de phrase %d hors limite" +#define MSG_BAD_PHRASE_NB "num‚ro de phrase hors limite %d rc=%d\n" +#define MSG_BAD_POS_CODE "POS_code invalide %d" +#define MSG_BAD_POS_TYPE "Type de POS_code invalide %d" +#define MSG_BAD_PROJNUM "Mauvais projnum %d pour la colonne %s" +#define MSG_BAD_QUERY_OPEN "Mode invalide %d pour l'ouverture d'une requˆte" +#define MSG_BAD_QUERY_TYPE "Type de requˆte %d invalide pour %s" +#define MSG_BAD_QUOTE_FIELD "Quote manquante dans %s champs %d ligne %d" +#define MSG_BAD_READ_NUMBER "Mauvais nombre %d de valeurs lues dans %s" +#define MSG_BAD_RECFM "Recfm type %d invalide pour DOSCOL" +#define MSG_BAD_RECFM_VAL "Valeur invalide %d de Recfm" +#define MSG_BAD_RESULT_TYPE "Mauvais type de r‚sultat %d pour %s" +#define MSG_BAD_RETURN_TYPE "Type de retour %d incorrect" +#define MSG_BAD_ROW_VALIST "Liste de valeurs invalide pour ROW" +#define MSG_BAD_ROW_VALNB "Nombre de valeurs in‚gal dans la liste" +#define MSG_BAD_SCF_ARGTYPE "Argument %d de type=%s invalide pour %s" +#define MSG_BAD_SEM_DOMAIN "Domain .%d invalide" +#define MSG_BAD_SETTINGS "Certaines sp‚cifications sont incompatibles avec le type de la table" +#define MSG_BAD_SET_CASE "La casse d'un tableau ne peut pas passer de non respect … respecter" +#define MSG_BAD_SET_STRING "SetValue: appel invalide pour STRING" +#define MSG_BAD_SET_TYPE "Set type %hd invalide" +#define MSG_BAD_SPECIAL_CMD "Commande sp‚ciale invalide" +#define MSG_BAD_SPECIAL_COL "Colonne sp‚ciale invalide %s" +#define MSG_BAD_SPEC_COLUMN "Colonne sp‚ciale invalide pour ce type de table" +#define MSG_BAD_SQL_PARAM "ParamŠtre SQL invalide pour FindColblk" +#define MSG_BAD_SUBLST_TYPE "Coparm: type %d de sous-liste invalide" +#define MSG_BAD_SUBSEL_IN_X "Sub-select invalide pour une expression" +#define MSG_BAD_SUBSEL_TYPE "Type %d invalide retourn‚ de Sub-Select" +#define MSG_BAD_SUB_RESULT "R‚sultat ind‚fini de fonction Sub-Select" +#define MSG_BAD_SUB_SELECT "Sub-select invalide comme argument de fonction" +#define MSG_BAD_TABLE_LINE "Ligne '%s' ill‚gale ou tronqu‚e dans la section Tables" +#define MSG_BAD_TABLE_LIST "Table %s absente de la liste des tables" +#define MSG_BAD_TABLE_TYPE "Type invalide %s pour la table %s" +#define MSG_BAD_TEST_TYPE "BlockTest sur tableau: types d‚pareill‚s %s %s" +#define MSG_BAD_TRIM_ARGTYP "Argument de type invalide pour Trim" +#define MSG_BAD_TYPE_FOR_IN "Types d'argument incompatibles pour la fonction IN" +#define MSG_BAD_TYPE_FOR_S "Type incorrecte %d pour %s(%d)" +#define MSG_BAD_TYPE_LIKE "Type(%d)= %d invalide pour LIKE" +#define MSG_BAD_UPD_COR "Le qualificateur %s de la colonne %s ne se refŠre pas … la table mise … jour %s" +#define MSG_BAD_USERBLK_LEN "Mauvaise longueur … l'‚criture du bloc utilisateur" +#define MSG_BAD_USETEMP "Usetemp invalide '%s'" +#define MSG_BAD_USETEMP_VAL "Valeur pour Usetemp invalide %d" +#define MSG_BAD_VALBLK_INDX "Valeur hors limites de l'index du bloc de valeurs" +#define MSG_BAD_VALBLK_TYPE "Type=%d invalide pour un bloc de valeurs" +#define MSG_BAD_VALNODE "Type %d invalide pour le noeud valeur colonne %s" +#define MSG_BAD_VALUE_TYPE "Type de valeur invalide %d" +#define MSG_BAD_VAL_UPDATE "Impossible de d‚terminer quelle valeur %s doit ˆtre mise … jour" +#define MSG_BAD_VIEW_OPEN "Mode invalide %d pour l'ouverture d'une View" +#define MSG_BAD_XMODE_VAL "Mode d'ex‚cution %d invalide" +#define MSG_BAD_XOBJ_TYPE "Mauvais type de Xobject %d" +#define MSG_BAS_NS_LIST "Format invalide de la liste des espace-noms" +#define MSG_BIN_F_TOO_LONG "Valeur trop longue pour le champ %s (%d --> %d)" +#define MSG_BIN_MODE_FAIL "Echec mode binaire: %s" +#define MSG_BLKTYPLEN_MISM "Disparit‚ types/longueurs de bloc dans SetValue" +#define MSG_BLK_IS_NULL "Blk est nul" +#define MSG_BLOCK_NO_MATCH "Bloc non correspondant" +#define MSG_BREAKPOINT "Point de controle" +#define MSG_BUFF_TOO_SMALL "GetColData: Buffer trop petit" +#define MSG_BUFSIZE_ERROR "Erreur en recherchant la taille du buffer" +#define MSG_BUILDING_GROUPS "Formation des groupes" +#define MSG_BUILD_DIST_GRPS "Formation des groupes distinctes" +#define MSG_BUILD_INDEX "Construction index %s sur %s" +#define MSG_BXP_NULL "Bxp nul dans PUTFON" +#define MSG_CANNOT_OPEN "Ouverture impossible de %s" +#define MSG_CD_ONE_STEP "Count Distinct doit ˆtre ex‚cut‚ en une seule ‚tape" +#define MSG_CD_ORDER_ERROR "Erreur de tri dans Count Distinct" +#define MSG_CHECKING_ROWS "Test des lignes … mettre … jour" +#define MSG_CHECK_LEVEL "Niveau de v‚rification fix‚ … %u" +#define MSG_CHSIZE_ERROR "Erreur dans chsize: %s" +#define MSG_CLN_NOT_IN_JOIN "La colonne C%d n'est pas dans le join" +#define MSG_CNTDIS_COL_LOST "Colonne du Count Distinct perdue" +#define MSG_COLIST_BAD_TYPE "Type=%d invalide pour Colist" +#define MSG_COLNAM_TOO_LONG "Nom de colonne trop long" +#define MSG_COLSEC_TOO_BIG "Section colonne trop grande, table %s (%d)" +#define MSG_COLS_REDUCED " (r‚duit par Maxcol)" +#define MSG_COLUMN_ERROR "Erreur de colonne" +#define MSG_COLUMN_MISMATCH "Colonne %s d‚pareill‚e" +#define MSG_COLUMN_NOT_KEY "La colonne jointe R%d.%s n'est pas une cl‚" +#define MSG_COL_ALLOC_ERR "Allocation impossible du noeud colonne" +#define MSG_COL_ALLOC_ERROR "Erreur d'allocation m‚moire pour la colonne %d" +#define MSG_COL_HAS_NO_DEF "La colonne %s n'est pas d‚finie" +#define MSG_COL_INVAL_TABLE "La colonne %s.%s n'existe pas dans la table %s alias %s" +#define MSG_COL_ISNOT_TABLE "La colonne %s n'est pas dans la table %s" +#define MSG_COL_NB_MISM "Le nombre de colonnes ne correspond pas" +#define MSG_COL_NOTIN_GRPBY "La colonne %s n'est pas dans la liste de Group By" +#define MSG_COL_NOTIN_TABLE "La colonne %s n'est dans aucune table" +#define MSG_COL_NOTIN_UPDT "%s n'appartient pas … la table mise … jour %s" +#define MSG_COL_NOT_CODED "La colonne %s n'est pas codifi‚e" +#define MSG_COL_NOT_EXIST "La colonne %s n'existe pas dans %s" +#define MSG_COL_NOT_FOUND "La colonne %s n'est pas dans la table %s" +#define MSG_COL_NOT_IN_DB "La colonne %s de la table %s n'est pas dans la base de donn‚es" +#define MSG_COL_NOT_IN_JOIN "La colonne %s n'est pas dans le join" +#define MSG_COL_NOT_SORTED "La colonne %s de la table %s n'est pas tri‚e" +#define MSG_COL_NUM_MISM "Disparit‚ du nombre de colonnes" +#define MSG_COL_USED_TWICE "Colonne %s utilis‚e deux fois ???" +#define MSG_COMPUTE_ERROR "Erreur dans Compute, op=%d" +#define MSG_COMPUTE_NIY "Compute non impl‚ment‚ pour TOKEN" +#define MSG_COMPUTING "Calculs en cours" +#define MSG_COMPUTING_DIST "Comptage des valeurs distinctes" +#define MSG_COMPUTING_FUNC "Calcul de(s) fonction(s)" +#define MSG_COM_ERROR "Erreur Com" +#define MSG_CONCAT_SUBNODE "Concat‚nation de sous-noeuds impossible" +#define MSG_CONNECTED "Connect‚e" +#define MSG_CONNECT_CANCEL "Connection interrompue par l'utilisateur" +#define MSG_CONNECT_ERROR "Erreur %d se connectant à %s" +#define MSG_CONN_CLOSED "%s(%d) ferm‚e" +#define MSG_CONN_CREATED "Connexion %s cr‚e" +#define MSG_CONN_DROPPED "Connexion %s supprim‚e" +#define MSG_CONN_OPEN "%s(%d) ouverte (%s)" +#define MSG_CONN_SUC_OPEN "%s(%d) ouverte avec succŠs" +#define MSG_CONTROL_C_EXIT "Exit par Ctrl-C" +#define MSG_COPY_BAD_PHASE "Copie de liste invalide en phase %d" +#define MSG_COPY_INV_TYPE "Coparm: type non support‚ %d" +#define MSG_CORREL_NO_QRY "Les sous-requˆtes corr‚l‚es ne peuvent pas ˆtre de type QRY" +#define MSG_CREATED_PLUGDB " Cr‚‚ par PlugDB %s " +#define MSG_CURSOR_SET "Curseur remis … %d" +#define MSG_DATABASE_ACTIVE "Base de donn‚es %s activ‚e" +#define MSG_DATABASE_LOADED "Base de donn‚es %s charg‚e" +#define MSG_DATA_IS_NULL "ExecSpecialCmd: data est NULL" +#define MSG_DATA_MISALIGN "Mauvais alignement pour ce type de donn‚es" +#define MSG_DBASE_FILE "Fichier dBASE dbf: " +#define MSG_DB_ALREADY_DEF "Base de donn‚es %s d‚j… d‚finie" +#define MSG_DB_ALTERED "Base de donn‚es modifi‚e" +#define MSG_DB_CREATED "Base de donn‚es %s cr‚‚e" +#define MSG_DB_NOT_SPEC "Base de donn‚es non sp‚cifi‚e" +#define MSG_DB_REMOVED "Base de donn‚es %s retir‚e de la liste" +#define MSG_DB_SORT_ERROR "Erreur de tri DB" +#define MSG_DB_STOPPED "Arrˆt de la base de donn‚es %s" +#define MSG_DEBUG_NOT_ACTIV "Mode Debug inactif" +#define MSG_DEBUG_SET_INV "Invalide pour Debug: %c" +#define MSG_DEF_ALLOC_ERROR "Erreur d'allocation de la classe DEF %s" +#define MSG_DELETING_ROWS "Suppression des lignes" +#define MSG_DEL_FILE_ERR "Erreur … l'effacement de %s" +#define MSG_DEL_READ_ERROR "Delete: erreur en lecture req=%d len=%d" +#define MSG_DEL_WRITE_ERROR "Delete: erreur en ‚criture: %s" +#define MSG_DEPREC_FLAG "Option Flag p‚rim‚e, utiliser Coltype" +#define MSG_DICTIONARY "Dictionnaire " +#define MSG_DIRECT_VARTOK "AccŠs direct aux rŠgles du Variable Token non impl‚ment‚" +#define MSG_DISCONNECTED "D‚connect‚" +#define MSG_DISTINCT_ERROR "Plus d'un ‚l‚ment fonctionel DISTINCT" +#define MSG_DISTINCT_ROWS "S‚lection des lignes distinctes" +#define MSG_DISTINCT_VALUES "Extraction des valeurs distinctes" +#define MSG_DIS_NOHEAD_JOIN "Jointure distincte sur une table non en tˆte" +#define MSG_DLL_LOAD_ERROR "Erreur %d au chargement du module %s" +#define MSG_DOMAIN_EMPTY "Le domaine %s est vide" +#define MSG_DOMAIN_ERROR "Colonne %s: disparit‚ domaine(%s)/valeur(%s)" +#define MSG_DOMAIN_FULL "Le domaine %s est plein (max=%d)" +#define MSG_DOM_FILE_ERROR "Fichier domain %s introuvable" +#define MSG_DOM_NOT_SUPP "MS-DOM non support‚ par cette version" +#define MSG_DOM_OPEN_ERROR "Erreur d'ouverture du domaine: %s" +#define MSG_DOM_READ_ERROR "Erreur %d en lecture de domaine: %s" +#define MSG_DOM_READ_ONLY "La table domaine %s est en lecture seulement" +#define MSG_DOM_WRITE_ERROR "Erreur %d en ‚criture de domaine: %s" +#define MSG_DONE "Effectu‚, rc=%d" +#define MSG_DOSALMEM_NOMEM "Erreur d'allocation, pas assez de m‚moire" +#define MSG_DROP_DB_ERR "Echec du Drop sur le base de donn‚es %s" +#define MSG_DSORT_LOG_ERROR "Kindex: Erreur logique de tri distincte" +#define MSG_DUMMY_NO_COLS "Les tables DUMMY ne peuvent pas avoir de colonne" +#define MSG_DUPLICAT_COUNT "Count sur plus d'une colonne" +#define MSG_DUP_COL_NAME "La colonne %s existe en double" +#define MSG_DUP_PROJNUM "Non unique projnum %d pour la colonne %s" +#define MSG_DVAL_NOTIN_LIST "Valeur %s non trouv‚e dans la liste des valeurs distinctes de la colonne %s" +#define MSG_EMPTY_DOC "Document vide" +#define MSG_EMPTY_FILE "%s du fichier vide %s: " +#define MSG_ENDSTR_MISMATCH "Fins de chaŒne et de noeud ne correspondent pas" +#define MSG_END_OF_DELETE "%d ligne(s) enlev‚e(s) en %.2lf sec" +#define MSG_END_OF_INSERT "%d ligne(s) ins‚r‚e(s) en %.2lf sec" +#define MSG_END_OF_QUERY "%d ligne(s) extraite(s) en %.2lf sec" +#define MSG_END_OF_UPDATE "%d ligne(s) modifi‚e(s) en %.2lf sec" +#define MSG_EOF_AFTER_LINE "Fin de fichier aprŠs la ligne %d" +#define MSG_EOF_INDEX_FILE "EOF lisant le fichier index" +#define MSG_ERASED " et effac‚e" +#define MSG_ERASE_FAILED " (‚chec de l'effacement)" +#define MSG_ERROR "Erreur" +#define MSG_ERROR_IN_LSK "Erreur %d dans lseek64" +#define MSG_ERROR_IN_SFP "Erreur %d dans SetFilePointer" +#define MSG_ERROR_NO_PARM "ParamŠtre absent (valide seulement pour %.8s.1 et %.8s.5)" +#define MSG_ERROR_OPENING "Erreur … l'ouverture de : " +#define MSG_ERR_NUM_GT_MAX "Erreur: Numval (%d) plus grand que Maxnum (%d)" +#define MSG_ERR_READING_REC "Erreur lisant l'enregistrement %d de %s" +#define MSG_ERR_RET_RULE "Retour erreur, rŠgle=%u" +#define MSG_ERR_RET_TYPE "Retour erreur, type=%d" +#define MSG_EVAL_EXPIRED "Cette version d'évaluation est expir‚e" +#define MSG_EVAL_ONLY "L'utilisation de cette Dll est pour ‚valuation seulement" +#define MSG_EXECUTING "Ex‚cution" +#define MSG_EXECUTION_ERROR "Erreur d'ex‚cution" +#define MSG_EXEC_MODE_IS "Le mode d'ex‚cution est %s" +#define MSG_EXEC_MODE_RESET ". Mode remis … Execute" +#define MSG_EXEC_MODE_SET "Mode d'ex‚cution fix‚ … %s" +#define MSG_EXIT_EVAL_ERR "Erreur pendant l'‚valuation de Exit" +#define MSG_EXIT_FROM_LANG "Fin du langage %s version %d.%d" +#define MSG_FAIL_ADD_NODE "L'ajout du noeud %s dans la table a ‚chou‚" +#define MSG_FETCHING_DATA "Recherche des donn‚es" +#define MSG_FETCHING_ROWS "Recherche des lignes" +#define MSG_FETCH_NO_RES "Fetch: Pas de R‚sultats" +#define MSG_FIELD_TOO_LONG "Valeur trop longue pour le champs %d ligne %d" +#define MSG_FILELEN_ERROR "Erreur dans %s pour %s" +#define MSG_FILE_CLOSE_ERR "Erreur %d … la fermeture du fichier" +#define MSG_FILE_IS_EMPTY "Le fichier %s est vide" +#define MSG_FILE_MAP_ERR "Erreur de File mapping" +#define MSG_FILE_MAP_ERROR "CreateFileMapping %s erreur rc=%d" +#define MSG_FILE_NOT_FOUND "Fichier %s introuvable" +#define MSG_FILE_OPEN_YET "Fichier %s d‚j… ouvert" +#define MSG_FILE_UNFOUND "Fichier %s non trouv‚" +#define MSG_FILGRP_NO_TABLE "Table %d manquante pour groupe filtre" +#define MSG_FILTER_ATTACH "Filtre pass‚ … Attach" +#define MSG_FILTER_NO_TABLE "Filtre: premiŠre table manquante" +#define MSG_FIND_BAD_TYPE "Recherche dans un tableau: type non conforme %s %s" +#define MSG_FIX_OVFLW_ADD "D‚passement de capacit‚ en addition" +#define MSG_FIX_OVFLW_TIMES "D‚passement de capacit‚ en mutiplication" +#define MSG_FIX_UNFLW_ADD "Sous d‚passement de capacit‚ en addition" +#define MSG_FIX_UNFLW_TIMES "Sous d‚passement de capacit‚ en multiplication" +#define MSG_FLD_TOO_LNG_FOR "Champs %d trop long pour %s ligne %d de %s" +#define MSG_FLTST_NO_CORREL "FilTest ne devrait ˆtre appel‚ que pour les sous-requˆtes corr‚l‚es" +#define MSG_FLT_BAD_RESULT "Virgule flottante: r‚sultat inexacte" +#define MSG_FLT_DENORMAL_OP "Op‚rande virgule flottante non normalis‚" +#define MSG_FLT_INVALID_OP "Op‚ration virgule flottante invalide" +#define MSG_FLT_OVERFLOW "D‚passement de capacit‚ virgule flottante" +#define MSG_FLT_STACK_CHECK "Virgule flottante: Erreur de la pile" +#define MSG_FLT_UNDERFLOW "Sous-d‚passement de capacit‚ virgule flottante" +#define MSG_FLT_ZERO_DIVIDE "Virgule flottante: division par z‚ro" +#define MSG_FMT_WRITE_NIY "L'‚criture des fichiers %s n'est pas encore impl‚ment‚e" +#define MSG_FNC_NOTIN_SLIST "Fonction de tri absente de la liste de s‚lection" +#define MSG_FORMAT_ERROR "Erreur de formattage" +#define MSG_FOXPRO_FILE "Fichier FoxPro: " +#define MSG_FPUTS_ERROR "Erreur dans fputs: %s" +#define MSG_FSBPARP_NULL "PUTFON: fsbparp est nul" +#define MSG_FSEEK_ERROR "Erreur dans fseek: %s" +#define MSG_FSETPOS_ERROR "Erreur dans fseek pour i=%d" +#define MSG_FTELL_ERROR "Erreur dans ftell enregistrement=%d: %s" +#define MSG_FUNCTION_ERROR "Erreur dans %s: %d" +#define MSG_FUNC_ERRNO "Erreur %d dans %s" +#define MSG_FUNC_ERROR "Erreur dans %s" +#define MSG_FUNC_ERR_S "Erreur dans %s: %s" +#define MSG_FUNC_REF_DEL "R‚f‚rence … une fonction d‚finie (rŠgle %d) qui a ‚t‚ supprim‚e" +#define MSG_FWRITE_ERROR "Erreur dans fwrite: %s" +#define MSG_GETCWD_ERR_NO "?getcwd %s errno=%d" +#define MSG_GETFILESIZE_ERR "Erreur %d dans GetFileSize" +#define MSG_GET_DIST_VALS "R‚cup‚ration des valeurs distinctes de " +#define MSG_GET_ERROR "Erreur dans %s (colonne %d)" +#define MSG_GET_FUNC_ERR "Erreur en recherche de la fonction %s: %s" +#define MSG_GET_NAME_ERR "Erreur en retrouvant le nom d'une table SYS" +#define MSG_GLOBAL_ERROR "Erreur d'allocation de Global (taille=%d)\n" +#define MSG_GRAM_ALLOC_ERR "Erreur d'allocation dans Grammar Up" +#define MSG_GRAM_MISMATCH "Avertissement: version de GRAMMAR p‚rim‚e (sauv‚ sous GRAMMAR v%u)" +#define MSG_GRAM_SUBSET_ERR "Erreur d'initialisation du dictionnaire de la grammaire" +#define MSG_GRBY_TAB_NOTIMP "Group by avec tables jointes non impl‚ment‚" +#define MSG_GROUPBY_NOT_ALL "Group By doit inclure toutes les s‚lections non-fonctionnelles" +#define MSG_GROUP_ON_FUNC "Group by invalide sur colonne fonctionnelle" +#define MSG_GRP_COL_MISM "Disparit‚ colonne des groupes" +#define MSG_GRP_LIST_MISMAT "Le groupement ne couvre pas la liste de s‚lection" +#define MSG_GUARD_PAGE "Violation de page de garde" +#define MSG_GZOPEN_ERROR "gzopen %s: erreur %d sur %s" +#define MSG_GZPUTS_ERROR "Erreur dans gzputs: %s" +#define MSG_HANDLE_IS_NULL "%s est NULL: erreur code: %d" +#define MSG_HARRY_COMP_NIY "Compute non impl‚ment‚ pour les chaŒnes cod‚es" +#define MSG_HAVING_FILTER "Traitement du Filtre Having" +#define MSG_HBUF_TOO_SMALL "Buffer(%d) trop petit pour entˆte(%d)" +#define MSG_HEAD_OPEN_ERROR "Erreur … l'ouverture du fichier header" +#define MSG_HEAD_READ_ERROR "Erreur en lecture du fichier header %s" +#define MSG_HEAD_WRITE_ERR "Erreur en ‚criture du fichier header" +#define MSG_HI_OFFSET_ERR "Offset sup‚rieur non nul" +#define MSG_HUGE_DEFAULT "Huge est %d par d‚fault" +#define MSG_HUGE_WARNING_1 "M‚moire Huge non compatible 16-bit pour %d\n" +#define MSG_HUGE_WARNING_2 "R‚sultats impr‚visibles possibles\n" +#define MSG_IDLE "Au repos" +#define MSG_ILLEGAL_INSTR "Instruction ill‚gale" +#define MSG_ILL_FILTER_CONV "Conversion implicite ill‚gale dans un filtre" +#define MSG_INDEX_CREATED "Index %s cr‚‚ sur %s" +#define MSG_INDEX_DEF_ERR "Erreur sauvegardant l'index d‚finition pour %s" +#define MSG_INDEX_DROPPED "Index %s supprim‚ de %s" +#define MSG_INDEX_INIT_ERR "Echec de l'initialisation de l'index %s" +#define MSG_INDEX_NOT_DEF "Index %s non d‚fini" +#define MSG_INDEX_NOT_UNIQ "L'index n'est pas Unique" +#define MSG_INDEX_ONE_SAVE "Les index sont sauvegard‚s dans un fichier unique" +#define MSG_INDEX_SEP_SAVE "Les index sont sauvegard‚s dans des fichiers s‚par‚s" +#define MSG_INDEX_YET_ON "L'index %s existe d‚j… sur %s" +#define MSG_INDX_ALL_DROP "Tous les index de %s supprim‚s" +#define MSG_INDX_COL_NOTIN "La colonne index %s n'existe pas dans la table %s" +#define MSG_INDX_EXIST_YET "L'entr‚e index existe d‚j…" +#define MSG_INIT_ERROR "Erreur à l'initialisation de %s" +#define MSG_INIT_FAILED "L'initialisation de %s a ‚chou‚" +#define MSG_INPUT "Entr‚e: " +#define MSG_INPUT_KEYBD_YET "L'entr‚e est d‚j… au clavier" +#define MSG_INSERTING "Insertion: " +#define MSG_INSERT_ERROR "Insert erreur: usage multiple du fichier %s" +#define MSG_INSERT_MISMATCH "Les listes colonne et valeur ne correspondent pas" +#define MSG_INTERNAL "interne" +#define MSG_INT_COL_ERROR "Erreur interne sur la colonne index %s" +#define MSG_INT_OVERFLOW "D‚passement de capacit‚ sur entier" +#define MSG_INT_ZERO_DIVIDE "Division entiŠre par z‚ro" +#define MSG_INVALID_BIP "Bip invalide .%d" +#define MSG_INVALID_DISP "Disposition invalide" +#define MSG_INVALID_FTYPE "SBV: Ftype %d invalide" +#define MSG_INVALID_HANDLE "Poign‚e invalide" +#define MSG_INVALID_OPER "Op‚rateur invalide %d pour %s" +#define MSG_INVALID_OPTION "Option invalide %s" +#define MSG_INV_COLUMN_TYPE "Type %d Invalide pour la colonne %s" +#define MSG_INV_COL_DATATYP "Type de donn‚es %d invalide pour la colonne %d" +#define MSG_INV_COL_NUM "Colonne invalide %d" +#define MSG_INV_COL_TYPE "Type de colonne %s invalide" +#define MSG_INV_CONC_BIP "Bip invalide (seuls valides: %.8s.0 .1 and .5)" +#define MSG_INV_DATA_PATH "Chemin vers les donn‚es invalide" +#define MSG_INV_DEF_READ "Lecture diff‚r‚e invalide rc=%d" +#define MSG_INV_DIRCOL_OFST "Offset invalide pour une colonne DIR" +#define MSG_INV_DOMAIN_TYPE "Type invalide %d" +#define MSG_INV_FILTER "Filtre r‚siduel dans %s" +#define MSG_INV_FNC_BUFTYPE "FNC: Type %d de l'argument invalide pour %s" +#define MSG_INV_INFO_TYPE "Type d'info catalog invalide %d" +#define MSG_INV_INIPATH "Inipath invalide " +#define MSG_INV_MAP_POS "Position m‚moire invalide" +#define MSG_INV_OPERATOR "op‚rateur invalide %d\n" +#define MSG_INV_PARAMETER "ParamŠtre invalide %s" +#define MSG_INV_PARM_TYPE "Type de paramŠtre invalide" +#define MSG_INV_QUALIFIER "Qalificateur '%s' invalide" +#define MSG_INV_QUERY_TYPE "Type de requˆte %d invalide" +#define MSG_INV_RAND_ACC "L'accŠs al‚atoire d'une table non optimis‚e est impossible" +#define MSG_INV_REC_POS "Position d'enregistrement invalide" +#define MSG_INV_RESULT_TYPE "Type de r‚sultat invalide %s" +#define MSG_INV_SET_SUBTYPE "Type de formattage %d invalide" +#define MSG_INV_SPECIAL_CMD "%s: Commande sp‚ciale invalide" +#define MSG_INV_SUBTYPE "Sous type invalide %s" +#define MSG_INV_TOK_DOMAIN "Le domaine %s n'existe pas" +#define MSG_INV_TOPSEM_CMD "Commande TopSem invalide %c" +#define MSG_INV_TRANSF_USE "Usage invalide en rŠgle transformationnelle" +#define MSG_INV_TYPE_SPEC "Sp‚cification de type invalide (%.8s.%d)" +#define MSG_INV_UPDT_TABLE "Table %s invalide pour Update" +#define MSG_INV_VALUE_LIST "Liste de valeurs invalide pour Insert" +#define MSG_INV_WHERE_JOIN "Clause Where invalide dans une requˆte de jointure" +#define MSG_INV_WORK_PATH "Chemin de travail invalide" +#define MSG_IN_ARGTYPE_MISM "Arguments de types incompatibles pour une expression IN" +#define MSG_IN_USE " et en activit‚" +#define MSG_IN_WITHOUT_SUB "IN ou EXISTS sans tableau ou subquery" +#define MSG_IS_NOT_CONN "%s n'est pas une connexion d‚finie" +#define MSG_JCT_MISS_COLS "Colonnes manquantes pour une table JCT" +#define MSG_JCT_MISS_TABLE "Table jointe manquante pour JCT" +#define MSG_JCT_NO_FILTER "Filtrage impossible des tables virtuelles JCT" +#define MSG_JCT_NO_KEY "Erreur logique JCT: cl‚ manquante" +#define MSG_JOIN_KEY_NO_COL "La cl‚ de jointure n'est pas une colonne" +#define MSG_KEY_ALLOC_ERR "Erreur d'allocation d'un bloc offset cl‚" +#define MSG_KEY_ALLOC_ERROR "Erreur d'allocation m‚moire, Klen=%d n=%d" +#define MSG_LANGUAGE_QUIT "%s lib‚r‚" +#define MSG_LANG_ACTIVE "Langage %s actif" +#define MSG_LANG_ALLOC_FAIL "PlugInitLang: Erreur d'allocation du bloc Lang" +#define MSG_LANG_ALREADY_UP "Langage d‚j… en ‚dition" +#define MSG_LANG_BAD_SAVE "Langage %s peut-ˆtre incorrectement sauvegard‚" +#define MSG_LANG_NOT_FREED "Langage %s non lib‚rable (pas dans la chaŒne principale)" +#define MSG_LANG_SAVED "Langage %s sauvegard‚" +#define MSG_LANG_WR_LEN_ERR "Erreur de longueur … l'‚criture du bloc Lang" +#define MSG_LDF_ALLOC_ERROR "Erreur d'allocation d'un LdfBlock" +#define MSG_LDF_RN_MISMATCH "LDF: d‚calage des num‚ros de rŠgle" +#define MSG_LDF_WLEN_ERROR "Erreur de longueur en ‚crivant LdfData" +#define MSG_LDF_W_LEN_ERROR "Erreur de longueur pour LdfData en ‚criture" +#define MSG_LIC_NO_MYSQL "Votre licence actuelle ne permet pas l'utilisation du type MYSQL" +#define MSG_LINEAR_ERROR "Erreur de lin‚arisation" +#define MSG_LINE_LENGTH "Largeur d'impression fix‚e … %d" +#define MSG_LINE_MAXLIN "Nombre de lignes de travail plafonn‚ … %d" +#define MSG_LINE_MAXRES "Nombre de lignes de r‚sultat plafonn‚ … %d" +#define MSG_LINE_MAXTMP "Nombre de lignes interm‚diaires plafonn‚ … %d" +#define MSG_LINE_TOO_LONG "La nouvelle ligne est trop longue" +#define MSG_LINJOINDB_ERROR "Erreur systŠme: appel incorrecte … LinJoinDB" +#define MSG_LIST "--Liste--" +#define MSG_LNG_NOT_IN_LIST "Le langage %s n'est pas dans la liste" +#define MSG_LOADING_DB "Chargement description de la BD" +#define MSG_LOADING_FAILED "Le chargement de %s a ‚chou‚" +#define MSG_LOAD_CDLL_ERROR "Erreur au chargement de ConnDll: rc=%d" +#define MSG_LOCSTRG_TOO_BIG "LOCSTRG: n trop grand ? (%d)\n" +#define MSG_LOGICAL_ERROR "%s: Erreur logique" +#define MSG_LRECL_TOO_SMALL "Lrecl trop petit (longueur en-tˆte = %d)" +#define MSG_MAC_NO_DELETE "Pas de suppression de lignes pour les tables MAC" +#define MSG_MAC_NO_INDEX "Pas d'accŠs direct aux tables MAC" +#define MSG_MAC_READ_ONLY "Les tables MAC sont en lecture seulement" +#define MSG_MAC_WIN_ONLY "Les tables MAC sont seulement sous Windows" +#define MSG_MAKE_EMPTY_FILE "G‚n‚ration du fichier vide %s: %s" +#define MSG_MAKING "G‚n‚ration" +#define MSG_MAKING_DISTINCT "Regroupement des valeures distinctes" +#define MSG_MALLOC_ERROR "Allocation m‚moire impossible par %s" +#define MSG_MALLOC_NULL "malloc retourne Null" +#define MSG_MAP_NO_MORE "Le type %s n'est plus support‚" +#define MSG_MAP_OBJ_ERR "Erreur %d … la fermeture du map objet" +#define MSG_MAP_VEC_ONLY "MAP Insert permis seulement pour les tables VEC Estimate" +#define MSG_MAP_VIEW_ERROR "MapViewOfFile %s erreur rc=%d" +#define MSG_MAXSIZE_ERROR "Maxsize incalculable sur table ouverte" +#define MSG_MAXTMP_TRUNCATE "R‚sultats interm‚diaires tronqu‚s par maxtmp=%d" +#define MSG_MAX_BITMAP "Taille maxi des bitmaps d'optimisation fix‚e … %d" +#define MSG_MEMSIZE_TOO_BIG "Erreur: memsize (%d) trop grand pour Length (%d)" +#define MSG_MEM_ALLOC_ERR "Erreur d'allocation m‚moire, taille %s = %d" +#define MSG_MEM_ALLOC_ERROR "Erreur d'allocation m‚moire" +#define MSG_MEM_ALLOC_YET "M‚moire d‚j… allou‚e" +#define MSG_METAFILE_NOTFND "Fichier Meta introuvable" +#define MSG_MISPLACED_QUOTE "Appostrophe mal plac‚e ligne %d" +#define MSG_MISSING "Manquant: Value=%p Argval=%p Builtin=%d" +#define MSG_MISSING_ARG "Argument manquant pour l'op‚rateur %d" +#define MSG_MISSING_COL_DEF "D‚finition des colonnes manquante" +#define MSG_MISSING_CONNECT "Connection #1 manquante" +#define MSG_MISSING_EOL "Fin de ligne manquante dans %s" +#define MSG_MISSING_FIELD "Champs %d manquant dans %s ligne %d" +#define MSG_MISSING_FNAME "Nom du fichier manquant" +#define MSG_MISSING_NODE "Noeud %s manquant dans %s" +#define MSG_MISSING_POS "POS code manquant" +#define MSG_MISSING_ROWNODE "Impossible de trouver le noeud de la ligne %d" +#define MSG_MISSING_SERV_DB "Indication serveur et/ou base de donn‚es manquante" +#define MSG_MISS_LEAD_COL "Colonne majeure %s manquante" +#define MSG_MISS_NAME_LRECL "Nom du fichier et/ou LRECL manquant" +#define MSG_MISS_TABLE_LIST "Liste des tables manquante" +#define MSG_MISS_VCT_ELMT "Taille de bloc vectoriel manquante (Elements)" +#define MSG_MIS_TAG_LIST "Liste des balises colonne manquante" +#define MSG_MKEMPTY_NIY "MakeEmptyFile: pas encore implement‚ pour Huge et Unix" +#define MSG_MOVE_INV_TYPE "MOVPARM: paramŠtre de type invalide %d" +#define MSG_MULT_DISTINCT "Distinct utilis‚ plus d'une fois" +#define MSG_MULT_KEY_ERROR "Erreur sur cl‚ multiple k=%d n=%d" +#define MSG_MUL_MAKECOL_ERR "Erreur logique dans TABMUL::MakeCol" +#define MSG_MYSQL_CNC_OFF "La connexion … MySQL est ferm‚e" +#define MSG_MYSQL_CNC_ON "La connexion … MySQL est ‚tablie" +#define MSG_MYSQL_NOT_SUP "Pas de support de MySQL dans cette version" +#define MSG_MY_CNC_ALREADY "La connexion … MySQL est d‚j… active" +#define MSG_NAME_CONV_ERR "Erreur de convertion du nom de noeud" +#define MSG_NAME_IS_USED "Le nom %s est d‚j… utilis‚" +#define MSG_NCOL_GT_MAXCOL "Trop de colonnes (%d > %d max)" +#define MSG_NEW_CHAR_NULL "new char(%d) retourne Null" +#define MSG_NEW_DOC_FAILED "Impossible de cr‚er le nouveau document" +#define MSG_NEW_RETURN_NULL "NULL renvoy‚ par New dans PlugEvalLike" +#define MSG_NEW_TABLE_ERR "La nouvelle table %s ne peut pas ˆtre charg‚e" +#define MSG_NEXT_FILE_ERROR "Erreur en recherche du fichier suivant. rc=%s" +#define MSG_NODEF_FROM_VIEW "Pas de d‚finition de table depuis une view" +#define MSG_NODE_FOR_CHAR "Noeud %s trouve au lieu d'un caractŠre" +#define MSG_NODE_SUBSET_ERR "Erreur d'initialisation de la zone Noeud %d" +#define MSG_NONCONT_EXCEPT "Exception non-continuable" +#define MSG_NON_DUP_HAVING "Clause Having dans une requˆte non fonctionelle" +#define MSG_NON_EVAL_SEM "Sem non ‚valu‚e: p_no=%d" +#define MSG_NOP_ZLIB_INDEX "L'indexage d'une table zlib non optimis‚e est impossible" +#define MSG_NOT_A_DBF_FILE "Le fichier n'a pas le format dBASE dbf " +#define MSG_NOT_ENOUGH_COLS "Pas assez de colonnes dans %s" +#define MSG_NOT_ENOUGH_MEM "M‚moire insuffisante pour cette op‚ration" +#define MSG_NOT_FIXED_LEN "Fichier %s non fixe, len=%d lrecl=%d" +#define MSG_NOT_IMPLEMENTED "Non implement‚: %.8s" +#define MSG_NOT_IMPL_JOIN "Pas impl‚ment‚ pour les jointures" +#define MSG_NOT_IMPL_SET "Pas impl‚ment‚ pour les op‚rateurs d'ensembles" +#define MSG_NOT_IMPL_YET "Pas encore implement‚" +#define MSG_NOT_LINEARIZED "Arborescence des tables non lin‚aris‚e" +#define MSG_NOT_MODIFIABLE " (non modifiable)" +#define MSG_NO_0DH_HEAD "0DH manquant en fin d'en-tˆte (dbc=%d)" +#define MSG_NO_ACTIVE_APPL "Pas d'application active" +#define MSG_NO_ACTIVE_DB "Pas de base de donn‚es active" +#define MSG_NO_ACTIVE_UDIC "Pas de dictionaire utilisateur actif" +#define MSG_NO_AGGR_FUNC "Fonction aggr‚g‚e %d ill‚gale … cet endroit" +#define MSG_NO_AREA_FILE "Fichier Area introuvable" +#define MSG_NO_AVAIL_RESULT "Pas de r‚sultat disponible" +#define MSG_NO_BIG_DELETE "D‚l‚tion Partielle non impl‚ment‚e pour les fichiers HUGE" +#define MSG_NO_CHAR_FROM "Conversion de type %d en caractŠres impossible" +#define MSG_NO_CLUSTER_COL "Pas de colonne optimisable" +#define MSG_NO_COL_ADDING "Ajouter des colonnes dans une d‚finition existante est impossible" +#define MSG_NO_COL_DEF_AS "La d‚finitions des colonnes est incompatible avec AS Select" +#define MSG_NO_COL_FOUND "La section colonne %s est vide" +#define MSG_NO_COL_IN_TABLE "La colonne %d n'est pas dans la table %s" +#define MSG_NO_COL_SECTION "Section colonne manquante pour la table %s" +#define MSG_NO_CONNECT_ADDR "Adresse de connection non sp‚cifi‚e" +#define MSG_NO_CONST_FILTER "Filtres constants non implement‚s" +#define MSG_NO_CURLY_BRKT "Pas d'accolade de fermeture" +#define MSG_NO_DATABASE "Base de donn‚es %s introuvable" +#define MSG_NO_DATE_FMT "Pas de format date pour le valblock de type %d" +#define MSG_NO_DBF_INSERT "Insert pas encore impl‚ment‚ pour les fichier DBF" +#define MSG_NO_DEF_FNCCOL "Colonne fonction par d‚faut introuvable" +#define MSG_NO_DEF_PIVOTCOL "Colonne pivot par d‚faut introuvable" +#define MSG_NO_DIR_INDX_RD "Pas d'accŠs directe des tables %s" +#define MSG_NO_DMY_DIR_ACC "Pas d'accŠs direct aux tables virtuelles DUMMY" +#define MSG_NO_DOM_DELETE "D‚l‚tion Partielle non impl‚ment‚e pour les domaines" +#define MSG_NO_DOM_MATCH "ChaŒne %.8s... non touv‚e dans le domaine %s" +#define MSG_NO_EDITED_LANG "Coparm: Pas de langage en ‚dition" +#define MSG_NO_EXP_LINK "Liaison par expression invalide pour une table JCT" +#define MSG_NO_EXT_FILTER "Le filtrage ne peut se r‚f‚rer … une autre table" +#define MSG_NO_EXT_UPDATE "Pas de mise … jour en r‚f‚rence … une autre table" +#define MSG_NO_FEAT_SUPPORT "%s non support‚ dans cette version" +#define MSG_NO_FILE_LIST "La table %s n'a pas de liste de fichiers" +#define MSG_NO_FLD_FORMAT "Format absent pour le champs %d de %s" +#define MSG_NO_FORMAT_COL "Type COLUMN informattable" +#define MSG_NO_FORMAT_TYPE "Le format ne peut pas ˆtre d‚fini … partir du type %d" +#define MSG_NO_FULL_JOIN "Jointures autoris‚es seulement … ‚galit‚ sur cl‚(s)" +#define MSG_NO_FUL_OUT_JOIN "Jointures externes complŠtes non support‚es" +#define MSG_NO_FUNC_ORDER "Tri non support‚ sur ‚l‚ment fonctionnel" +#define MSG_NO_HEAD_JOIN "Jointure sur une table non en tˆte" +#define MSG_NO_HQL_CONV "Conversion en HQL non disponible" +#define MSG_NO_INDEX "La table %s n'a pas d'index" +#define MSG_NO_INDEX_GBX "Pas ou mauvais index pour SQLGBX" +#define MSG_NO_INDEX_IN "Pas d'index dans %s" +#define MSG_NO_INDEX_READ "Pas d'accŠs directe des tables multiples" +#define MSG_NO_INIT_LANG "Pas de langage initial" +#define MSG_NO_JOIN_TO_EXP "Jointure vers une expression impossible" +#define MSG_NO_JOIN_UPDEL "Pas de jointure avec Update/Delete" +#define MSG_NO_KEY_COL "Pas de colonne cl‚ trouv‚e" +#define MSG_NO_KEY_UPDATE "Le nom des cl‚s ne peut pas ˆtre modifi‚" +#define MSG_NO_LANGUAGE "Pas de langage op‚rationnel\n" +#define MSG_NO_LANG_TO_QUIT "Pas de langage … quitter" +#define MSG_NO_LISTVAL_HERE "LSTBLK: Liste de valeurs utilis‚e hors contexte" +#define MSG_NO_MAP_INSERT "MAP incompatible avec Insert" +#define MSG_NO_MATCHING_COL "Pas de colonne correspondant … %s dans %s" +#define MSG_NO_MATCH_COL "Colonne correspondante introuvable" +#define MSG_NO_MEMORY "M‚moire pleine" +#define MSG_NO_MEM_CORR_SUB "Subquery corr‚l‚e en m‚moire non encore impl‚ment‚e" +#define MSG_NO_MODE_PADDED "Mode non support‚ pour les fichiers 'padded'" +#define MSG_NO_MORE_COL "La colonne %s n'est plus dans la table pivot" +#define MSG_NO_MORE_LANG "Plus de langage, exit de %s\n" +#define MSG_NO_MORE_VAR "Les fichiers VAR ne sont plus support‚s" +#define MSG_NO_MULCOL_JOIN "Jointure vers un index multi-colonne pas encore possible" +#define MSG_NO_MULT_HAVING "Clauses Having multiples non impl‚ment‚es" +#define MSG_NO_MUL_DIR_ACC "AccŠs direct des tables multiples pas encore impl‚ment‚" +#define MSG_NO_MUL_VCT "Les tables VCT ne peuvent pas ˆtre multiples" +#define MSG_NO_MYSQL_CONN "Aucune connexion MySQL ouverte" +#define MSG_NO_MYSQL_DELETE "Pas de Delete pour les tables MySQL" +#define MSG_NO_NBCOL "Pas de NBcol" +#define MSG_NO_NBLIN "Pas de NBlin, MaxSize ou Continued" +#define MSG_NO_NBLIN_CONT "Fetch: Pas de NBlin ou Continued" +#define MSG_NO_NULL_CONST "Les constantes <null> ne sont pas prises en charge" +#define MSG_NO_ODBC_COL "Colonnes ODBC automatiques non support‚es par cette version" +#define MSG_NO_ODBC_DELETE "Delete ne devrait pas ˆtre appel‚ pour les tables ODBC" +#define MSG_NO_ODBC_DIRECT "AccŠs directe des tables ODBC non encore impl‚ment‚" +#define MSG_NO_ODBC_MUL "Multiple(2) non support‚ pour les tables ODBC" +#define MSG_NO_ODBC_SPECOL "Pas de colonne sp‚ciale ODBC" +#define MSG_NO_OPT_COLUMN "Pas optimisable ou pas de colonne optimis‚es" +#define MSG_NO_OP_MODIF "Les modificateurs ne s'appliquent pas … %s" +#define MSG_NO_PARAMETER "Pas de paramŠtre" +#define MSG_NO_PART_DEL "Delete partiel des fichier %s impossible" +#define MSG_NO_PART_MAP "Mapping partiel non impl‚ment‚ pour cet OS" +#define MSG_NO_PAR_BLK_INS "Insertion de bloc partiel impossible" +#define MSG_NO_PIV_DIR_ACC "Pas d'accŠs directe aux tables PIVOT" +#define MSG_NO_POS_ADDED "Pos_code non ajout‚" +#define MSG_NO_PROMPTING "Relance impossible pour les tables distribu‚es" +#define MSG_NO_QRY_DELETE "Delete n'est pas utilisable pour les views QRY" +#define MSG_NO_QUERY_ARRAY "Tableaux avec QUERY non encore impl‚ment‚s" +#define MSG_NO_RCUR_DSK_YET "Usage r‚cursif de DISK non encore implement‚" +#define MSG_NO_READ_32 "Lecture de 32 octets impossible" +#define MSG_NO_RECOV_SPACE "Espace non recouvrable dans le fichier index" +#define MSG_NO_REF_DELETE "Pas de suppression en r‚f‚rence … une autre table" +#define MSG_NO_REF_UPDATE "Pas de mise … jour en r‚f‚rence … une autre table" +#define MSG_NO_REMOTE_FNC "Certaines fonctions ne peuvent pas ˆtre ex‚cut‚es … distance" +#define MSG_NO_ROWID_FOR_AM "AccŠs direct impossible de ROWID pour les tables de type %s" +#define MSG_NO_ROW_NODE "Le nom du Rownode n'est pas d‚fini" +#define MSG_NO_SECTION_NAME "Nom de section manquant" +#define MSG_NO_SEC_UPDATE "Les noms de section ne peuvent pas ˆtre modifi‚s" +#define MSG_NO_SELECTED_DB "Aucune base de donn‚es s‚lect‚e" +#define MSG_NO_SELF_PIVOT "Une table ne peut se pivoter elle-mˆme !" +#define MSG_NO_SERVER_FOUND "Serveur introuvable" +#define MSG_NO_SETPOS_YET "SetPos pas encore impl‚ment‚ pour les fichier %s" +#define MSG_NO_SFEXIT_UNIX "Fonction %s non disponible sur Unix" +#define MSG_NO_SOURCE " (pas de source)" +#define MSG_NO_SPEC_COL "Pas de colonne sp‚ciales MYSQL" +#define MSG_NO_SQL_DELETE "Delete n'est pas utilisable actuellement pour les views SQL" +#define MSG_NO_SUB_VAL "Pas de sous-value d'un tableau de type %d" +#define MSG_NO_SUCH_INDEX "La table %s n'a pas l'index %s" +#define MSG_NO_SUCH_SERVER "Serveur %s introuvable" +#define MSG_NO_SUCH_TABLE "Table %s pas dans la base de donn‚es" +#define MSG_NO_TABCOL_DATA "Pas de donn‚es pour la table %s colonne %s" +#define MSG_NO_TABLE_COL "Aucune colonne trouv‚e pour %s" +#define MSG_NO_TABLE_DEL "Delete non autoris‚ pour les tables %s " +#define MSG_NO_TABLE_DESC "Pas de bloc descriptif de table" +#define MSG_NO_TABLE_INDEX "La table %s n'a pas d'index" +#define MSG_NO_TABLE_LIST "Pas de liste de tables" +#define MSG_NO_TAB_DATA "Pas de donn‚es pour la table %s" +#define MSG_NO_TERM_IN_TOK "Les non-terminaux ne sont pas utilisables dans les rŠgles de Token" +#define MSG_NO_TOKEN_DB "DB introuvable pour la colonne TOKEN %s" +#define MSG_NO_UNIX_CATINFO "Pas d'info catalogue sous Unix" +#define MSG_NO_UPDEL_JOIN "Pas de jointure de tables ODBC pour Update/Delete" +#define MSG_NO_VCT_DELETE "D‚l‚tion Partielle non impl‚ment‚e pour les fichiers VCT" +#define MSG_NO_VIEW_COLDEF "Colonne d‚finition impossible pour les views" +#define MSG_NO_VIEW_SORT "La View fonctionnelle %s ne peut pas ˆtre tri‚e ou jointe" +#define MSG_NO_ZIP_DELETE "Delete sur fichier Zip non encore implement‚" +#define MSG_NO_ZIP_DIR_ACC "AccŠs directe des tables ZDOS non encore implement‚" +#define MSG_NULL_COL_VALUE "La colonne n'a pas de valeur" +#define MSG_NULL_ENTRY "InitLang, entr‚e nulle %d %s" +#define MSG_NULL_QUERY "Requˆte vide" +#define MSG_NUMVAL_NOMATCH "Disparit‚ de Numval pour %s" +#define MSG_N_FULL_PARSES "%d significations" +#define MSG_ODBC_READ_ONLY "ODBC est actuellement en lecture seulement" +#define MSG_OFFSET_NOT_SUPP "Offset non support‚ pour ce type de sous requˆte" +#define MSG_ONE_LANG_YET "Un langage est d‚j… en ‚dition" +#define MSG_ONE_PARAM_ONLY "Un seul paramŠtre autoris‚" +#define MSG_ONLY_LOG10_IMPL "Seul Log10 est implement‚" +#define MSG_ON_LANGUAGE "Langage %.8s version %d niveau %d ‚ditable" +#define MSG_OPENING "Ouverture" +#define MSG_OPENING_QUERY "Ouverture de la requˆte" +#define MSG_OPEN_EMPTY_FILE "Ouverture du fichier vide %s: %s" +#define MSG_OPEN_ERROR "Erreur d'ouverture %d en mode %d sur %s: " +#define MSG_OPEN_ERROR_IS "Erreur … l'ouverture de %s: %s" +#define MSG_OPEN_ERROR_ON "Erreur d'ouverture sur %s" +#define MSG_OPEN_MODE_ERROR "Erreur d'ouverture(%s) %d sur %s" +#define MSG_OPEN_SORT_ERROR "Erreur logique de tri dans QUERY Open" +#define MSG_OPEN_STRERROR "Erreur … l'ouverture: %s" +#define MSG_OPEN_W_ERROR "Erreur … l'ouverture de %s en ‚criture" +#define MSG_OPTBLK_RD_ERR "Erreur … la lecture d'un bloc optimisation: %s" +#define MSG_OPTBLK_WR_ERR "Erreur … l'‚criture d'un bloc optimisation: %s" +#define MSG_OPTIMIZING "Optimisation de " +#define MSG_OPT_BMAP_RD_ERR "Erreur en lecture des bitmaps d'optimisation: %s" +#define MSG_OPT_BMAP_WR_ERR "Erreur en ‚criture des bitmaps d'optimisation: %s" +#define MSG_OPT_CANCELLED "Optimisation interrompue par l'utilisateur" +#define MSG_OPT_DVAL_RD_ERR "Erreur en lecture des valeurs distinctes: %s" +#define MSG_OPT_DVAL_WR_ERR "Erreur en ‚criture des valeurs distinctes: %s" +#define MSG_OPT_HEAD_RD_ERR "Erreur en lecture de l'entˆte du fichier opt: %s" +#define MSG_OPT_HEAD_WR_ERR "Erreur en ‚criture de l'entˆte du fichier opt: %s" +#define MSG_OPT_INIT "Optimisation initialis‚e" +#define MSG_OPT_LOGIC_ERR "Erreur logique dans SetBitmap, i=%d" +#define MSG_OPT_MAX_RD_ERR "Erreur en lecture des valeurs maxi: %s" +#define MSG_OPT_MAX_WR_ERR "Erreur en ‚criture des valeurs maxi: %s" +#define MSG_OPT_MIN_RD_ERR "Erreur en lecture des valeurs mini: %s" +#define MSG_OPT_MIN_WR_ERR "Erreur en ‚criture des valeurs mini: %s" +#define MSG_OPT_NOT_MATCH "Le fichier opt %s n'est pas … jour" +#define MSG_OP_RES_TOO_LONG "R‚sultat trop long pour l'op‚rateur=%d" +#define MSG_ORDER_OUT_RANGE "Tri: Order %d hors limite" +#define MSG_ORDER_TWICE "Un mˆme ‚l‚ment est tri‚ deux fois" +#define MSG_PAGE_ERROR "Erreur de pagination" +#define MSG_PARM_CNT_MISS "Disparit‚ du nombre de ParamŠtres" +#define MSG_PARSE_NULL_SEM "S‚mantique nulle" +#define MSG_PARSING_QUERY "Analyse de la requˆte" +#define MSG_PIX_ERROR "Pix %s erreur rŠgle no=%u\n" +#define MSG_PIX_TEST_ERROR "RŠgle=%u: pix-TEST pas dans le premier noeud\n" +#define MSG_PLG_READ_ONLY "PLG est actuellement en lecture seulement" +#define MSG_PLM_NULL_SFP "TABPLM ReadDB: Sfp est NULL" +#define MSG_PLUG_NOT_INIT "Plug n'est pas initialis‚\n" +#define MSG_PLUG_NOT_RUN "Plug n'est pas en marche" +#define MSG_PNODE_RULE "(Noeud %d rŠgle %d) " +#define MSG_POS_TOO_LONG "%s trop long (>%d)" +#define MSG_PREC_VBLP_NULL "ARRAY SetPrecision: Vblp est NULL" +#define MSG_PRIV_INSTR "Instruction privil‚gi‚e" +#define MSG_PROCADD_ERROR "Erreur %d sur l'adresse de %s" +#define MSG_PROCESS_SUBQRY "Sub-Query en cours de traitement" +#define MSG_PROC_WOULD_LOOP "Bouclage du traitement (maxres=%d maxlin=%d)" +#define MSG_PROGRESS_INFO "Informations sur le traitement en cours" +#define MSG_PROMPT_CANCEL "Relance annul‚e" +#define MSG_PROMPT_NIY "Prompt non impl‚ment‚ pour cette configuration" +#define MSG_PTR_NOT_FOUND "Pointeur introuvable Num=%d ti1=%d" +#define MSG_PXDEF_IS_NULL "Pxdef est NULL" +#define MSG_QRY_READ_ONLY "Les views QRY sont en lecture seulement" +#define MSG_QUERY_CANCELLED "Requˆte interrompue par l'utilisateur" +#define MSG_QUERY_NOT_EXEC "Requˆte non ex‚cut‚e" +#define MSG_QUERY_SAVED "Requˆte %s sauvegard‚e" +#define MSG_QUOTE_IN_QUOTE "Appostrophe dans un champ entre appostrophe ligne %d" +#define MSG_RANGE_NIY "Range pas encore impl‚ment‚ pour %s" +#define MSG_RANGE_NO_JOIN "Range non compatible avec les index de jointure" +#define MSG_RC_READING "rc=%d en lecture de la table %s" +#define MSG_READB_BAD_INIT "%s ReadDB appel‚ avec Init=0" +#define MSG_READCOL_ERROR "SQLCOL: erreur dans ReadColumn" +#define MSG_READING "Lecture" +#define MSG_READING_FROM "Lecture de %s" +#define MSG_READING_RECORD "Erreur en lecture de l'enregistrement %d de %s" +#define MSG_READY "Prˆt" +#define MSG_READ_ERROR "Erreur en lecture sur %s: %s" +#define MSG_READ_ERROR_RC "Erreur en lecture, rc=%d" +#define MSG_READ_MEM_ERROR "Lecture m‚moire %d: taille=%d" +#define MSG_READ_ONLY "Cette table prot‚g‚e en lecture seule ne peut ˆtre modifi‚e" +#define MSG_READ_SEEK_ERROR "Erreur de recherche en lecture: %s" +#define MSG_READ_SEG_ERROR "Lecture segment %d: taille=%d" +#define MSG_RECEIVED "Re‡u %c\n" +#define MSG_RECORD_ERROR "Erreur … la lecture de l'enregistrement %d de %s" +#define MSG_RECORD_NO_SEP "Enregistrement sans s‚parateur" +#define MSG_REC_SKIPPED " (%d lignes erronn‚es saut‚es par l'option MaxErr)" +#define MSG_REDUCE_INDEX "Réduction de l'index" +#define MSG_REGISTER_ERR "Enregistrement NS impossible, pr‚fix='%s' et href='%s'" +#define MSG_REMOTE_CONN_ERR "La connection ‚loign‚e a ‚chou‚" +#define MSG_REMOVE_ERROR "Erreur en supprimant %s: %s" +#define MSG_REMOVE_NOT_IMPL "Remove non impl‚ment‚ pour TDB non Table" +#define MSG_RENAME_ERROR "Erreur renommant %s en %s: %s" +#define MSG_RENUM_RULES "Renum‚rotez les rŠgles et r‚entrez ADD (rŠgle sauvegard‚e dans la zone tampon)" +#define MSG_REORDER_INDEX "Reclassement de l'index" +#define MSG_REQU_ARG_NUM "La fonction %s doit avoir %d arguments" +#define MSG_RESET_TO "%s remis … %d" +#define MSG_RES_NOT_UNIQUE "Le r‚sultat n'est pas unique" +#define MSG_RET_FROM_LANG "Retour au language %s version %d.%d du language %s version %d.%d" +#define MSG_ROWID_NOT_IMPL "RowNumber non impl‚ment‚ pour les tables de type %s" +#define MSG_ROWS_SELECTED "%d lignes s‚lectionn‚es en %.2lf sec" +#define MSG_ROWS_TRUNCATED " (tronqu‚ par MAXRES, LIMIT, FREQ ou AreaSize)" +#define MSG_ROW_ARGNB_ERR "ROW: disparit‚ du nombre d'arguments (%d,%d)" +#define MSG_RPC_SERVER_ERR "Erreur logique dans TABMUL::MakeCol" +#define MSG_RSC_ALLOC_ERROR "Erreur d'allocation m‚moire dans Rescol %s" +#define MSG_RULE_ENTERED "RŠgle %d entr‚e" +#define MSG_RULE_SUBSET_ERR "Erreur d'initialisation de la zone RŠgles" +#define MSG_SAVING_INDEX "Sauvegarde du fichier index" +#define MSG_SCAN_NOT_IMP "Scan non impl‚ment‚" +#define MSG_SEC_KEY_FIRST "Les sections et cl‚s doivent ˆtre ins‚r‚es en premier" +#define MSG_SEC_NAME_FIRST "Le nom de section doit ˆtre en tˆte de liste en insertion" +#define MSG_SEC_NOT_FOUND "Section %s absente de %s" +#define MSG_SEEK_ERROR "Seek erreur dans CopyHeader" +#define MSG_SEMANTIC_TREE "Arbre s‚mantique" +#define MSG_SEM_BAD_REF "Sem @%d r‚f‚rence un argument de type non 0 ou 1" +#define MSG_SEM_UNKNOWN "inconnue, rc=%d" +#define MSG_SEP_IN_FIELD "Le champ %d contient le caractŠre s‚parateur" +#define MSG_SEQUENCE_ERROR "HSTMT: Allocation hors s‚quence" +#define MSG_SETEOF_ERROR "Erreur %d dans SetEndOfFile" +#define MSG_SETRECPOS_NIY "SetRecpos non impl‚ment‚ pour ce type de table" +#define MSG_SET_LOCALE "Locale fix‚e … %s" +#define MSG_SET_NULL_DOM "Valeur %d donn‚e … un domaine nul" +#define MSG_SET_OP_NOT_IMPL "Op‚rateurs ensemblistes non impl‚ment‚s" +#define MSG_SET_STR_TRUNC "SetValue: ChaŒne de caractŠres tronqu‚e" +#define MSG_SEVERAL_TREES "Jointure non sp‚cifi‚e pour certaines tables" +#define MSG_SFP_ERROR "Erreur sur SetFilePointer: %s" +#define MSG_SFUNC_NOT_IMPL "Fonction scalaire %s non impl‚ment‚e" +#define MSG_SHARED_LIB_ERR "Erreur au chargement de la librairie partag‚e %s: %s" +#define MSG_SINGLE_STEP "Pas … pas" +#define MSG_SLEEP "J'ai dormi %d milliseconds" +#define MSG_SMART_SORTING "R‚cup‚ration des lignes tri‚es (passage %d de %d)" +#define MSG_SMART_SORT_ERR "Erreur logique 1 dans Smart Sort" +#define MSG_SORTING "Tri en cours" +#define MSG_SORTING_INDEX "Tri de l'index" +#define MSG_SORTING_VAL "Tri de %d valeurs" +#define MSG_SORT_JOIN_INDEX "Tri de l'index de jointure" +#define MSG_SPCOL_READONLY "La colonne sp‚ciale %s est en lecture seulement" +#define MSG_SPEC_CMD_SEP "Les commandes sp‚ciales doivent ˆtre ex‚cut‚es s‚par‚ment" +#define MSG_SQL_BAD_TYPE "RephraseSQL: type %d non support‚" +#define MSG_SQL_BLOCK_MISM "CheckColumn: bloc SQL courant non correspondant" +#define MSG_SQL_CONF_ERROR "Erreur SQL: SQL_CONFORMANCE" +#define MSG_SQL_READ_ONLY "Les views SQL sont actuellement en lecture seulement" +#define MSG_SRCH_CLOSE_ERR "Erreur … la fermeture de l'Handle de recherche" +#define MSG_SRC_TABLE_UNDEF "La table source n'est pas d‚finie" +#define MSG_STACK_ERROR "Erreur sur la pile, i=%d\n" +#define MSG_STACK_OVERFLOW "Parser: D‚bordement de la pile\n" +#define MSG_STRG_NOT_FOUND "ChaŒne introuvable" +#define MSG_STRING_INV_LIST "Liste invalide pour SemString" +#define MSG_STRING_TOO_BIG "ChaŒne trop grande pour le domaine %s" +#define MSG_SUBALLOC_ERROR "Pas assez de m‚moire en zone %p pour allouer %d (utilis‚=%d libre=%d)" +#define MSG_SUBAL_HUGE_ERR "Pas assez de m‚moire en zone huge %p pour allouer %d" +#define MSG_SUBARG_NOSEM "Argument @ ou sous-phrase de niveau %d pointe sur un noeud sans Sem" +#define MSG_SUBARG_OUTRANGE "Argument @ ou sous-phrase de niveau %d hors limite" +#define MSG_SUBQRY_ONEITEM "Une Sub-Query ne doit avoir qu'une s‚lection" +#define MSG_SUBSET_ERROR "SubSet erreur dans LoadDB" +#define MSG_SUB_OPEN_YET "Subquery d‚j… ouverte" +#define MSG_SUB_RES_TOO_LNG "R‚sultat trop long pour SUBSTR" +#define MSG_SYNTAX_ERROR "Erreur de syntaxe" +#define MSG_SYSTEM_ERROR "Erreur systŠme %d" +#define MSG_S_ACCESS_DENIED "%s: accŠs non autoris‚" +#define MSG_S_ERROR "%s erreur" +#define MSG_S_ERROR_NUM "%s: erreur=%d" +#define MSG_S_INTRUPT_ERROR "%s: erreur interruption" +#define MSG_S_INVALID_PARM "%s: paramŠtre invalide" +#define MSG_S_INV_ADDRESS "%s: adresse invalide" +#define MSG_S_UNKNOWN_ERROR "%s: erreur de code %u inconnu" +#define MSG_TABDIR_READONLY "Les tables DIR sont en lecture seulement" +#define MSG_TABLE_ALREADY "La table %s existe d‚j…" +#define MSG_TABLE_ALTERED "Table %s %s alt‚r‚e" +#define MSG_TABLE_CREATED "%s table %s cr‚‚e" +#define MSG_TABLE_DROPPED "Table %s supprim‚e" +#define MSG_TABLE_MULT_JOIN "Utilisation multiple de la table %s pour jointure" +#define MSG_TABLE_NOT_IN_DB "La table %s n'existe pas dans %s" +#define MSG_TABLE_NOT_OPT "Table non optimisable" +#define MSG_TABLE_NO_INDEX "La table %s n'est pas indexable" +#define MSG_TABLE_NO_OPT "La table %s n'existe pas ou de type non optimisable" +#define MSG_TABLE_READ_ONLY "Les tables %s sont en lecture seulement " +#define MSG_TABMUL_READONLY "Les tables multiples sont en lecture seulement" +#define MSG_TAB_NOT_LOADED " (certaines tables n'ont put ˆtre charg‚es)" +#define MSG_TAB_NOT_SPEC "Table non specifi‚e" +#define MSG_TB_VW_NOTIN_DB "Table ou view %s pas dans la base de donn‚es" +#define MSG_TDB_NXT_NOT_NUL "Tdb.Next non NULL" +#define MSG_TDB_USE_ERROR "Erreur, Tdbp->Use=%d" +#define MSG_TOO_MANY_COLS "Trop de colonnes" +#define MSG_TOO_MANY_COLTAB "Trop de colonnes dans %s (%d)" +#define MSG_TOO_MANY_FIELDS "Trop de champs ligne %d de %s" +#define MSG_TOO_MANY_JUMPS "Trop de niveaux de saut" +#define MSG_TOO_MANY_KEYS "Trop de cl‚s (%d)" +#define MSG_TOO_MANY_POS "Trop de pos_codes" +#define MSG_TOO_MANY_TABLES "Trop de tables (%d)" +#define MSG_TOPSEM_ERROR "Erreur inconnue dans TopSem" +#define MSG_TO_BLK_IS_NULL "To Blk est nul" +#define MSG_TO_FTR_NOT_NULL "Set.To_Ftr n'est pas nul" +#define MSG_TO_PIX_NOT_NULL "Set.To_Pix n'est pas nul" +#define MSG_TO_SEM_NOT_NULL "Set.To_Sem n'est pas nul" +#define MSG_TRUNCATE_ERROR "Erreur en troncation: %s" +#define MSG_TRUNC_BY_ESTIM "Tronqu‚ par l'option Estimate" +#define MSG_TYPES_ERROR "Erreur sur Types(%d)" +#define MSG_TYPE_CONV_ERROR "Type non convertible dans une expression" +#define MSG_TYPE_DEF_MISM "Disparit‚ entre type et d‚finition" +#define MSG_TYPE_MISMATCH "Cl‚ et source ne sont pas du mˆme type" +#define MSG_TYPE_RECFM_MISM "Disparit‚ entre Type et Recfm" +#define MSG_TYPE_TO_VERIFY "Type … v‚rifier: %d" +#define MSG_TYPE_VALUE_ERR "Colonne %s: disparit‚ type(%s)/valeur(%s)" +#define MSG_UNBALANCE_QUOTE "Appostrophe en trop ligne %d" +#define MSG_UNDEFINED_AM "COLBLK %s: m‚thode d'accŠs ind‚finie" +#define MSG_UNDEFINED_PATH "Chemin d'accŠs ind‚fini pour Plgcnx.ini" +#define MSG_UNDEF_COL_COUNT "Count sur colonne non d‚finie" +#define MSG_UNKNOWN_DOMAIN "Domaine inconnu %s" +#define MSG_UNKNOWN_ERROR "Erreur inconnue" +#define MSG_UNKNOWN_EXCPT "Exception non r‚pertori‚e" +#define MSG_UNKNOWN_NAME "Nom inconnu: %.8s" +#define MSG_UNKNOWN_PATH "Chemin d'accŠs inconnu pour Plgcnx.ini" +#define MSG_UNKNOWN_POS "Nom pos_code inconnu: %s" +#define MSG_UNKNOWN_SEM "Sem %.8s inconnue, rc=%d" +#define MSG_UNKNOWN_SYNONYM "Synonyme inconnu" +#define MSG_UNKNW_QRY_TYPE "ReadDB: type de requˆte inconnu" +#define MSG_UNKN_ERR_CODE "Erreur de code %d inconnu" +#define MSG_UNLOADABLE " inchargeable: " +#define MSG_UNLOADABLE_PRM "%s inchargeable: %s" +#define MSG_UNMATCH_FIL_ARG "Argument de filtre d‚pareill‚" +#define MSG_UNQ_COL_SEV_TAB "La colonne %s non qualifi‚e est dans plusieurs tables" +#define MSG_UNRESOLVED_ARG "?Argument manquant: %s non r‚solu en %d ligne %d" +#define MSG_UPDATE_ERROR "Erreur en Update sur %s" +#define MSG_UPDATING_ROWS "Mise … jour des lignes" +#define MSG_UPD_ZIP_NOT_IMP "Mise … jour des tables ZDOS non encore implement‚" +#define MSG_UP_LANGUAGE "Bloc langage %.8s version %d niveau %d charg‚" +#define MSG_USED_FREE_MEM "Sarea: utilis‚ %d, libre %d" +#define MSG_USETEMP_IS "Usetemp est : %s" +#define MSG_USETEMP_RESET ". Usetemp remis … Auto" +#define MSG_USETEMP_SET "Usetemp fix‚ … %s" +#define MSG_USE_NO_MATCH "Use non correspondant : Use=%d, ti2=%d, ti3=%d" +#define MSG_USING_INDEX " (Index‚ par" +#define MSG_VALIST_MISMATCH "Disparit‚ des listes de valeurs" +#define MSG_VALSTR_TOO_LONG "Valeur %s trop longue pour une chaŒne de longueur %d" +#define MSG_VALTYPE_NOMATCH "Disparit‚ types de valeur" +#define MSG_VALUE_ERROR "Colonne %s: bloc valeur nul" +#define MSG_VALUE_NOT_ALLOC "Valeur non allou‚e pour la colonne R%d %s" +#define MSG_VALUE_TOO_BIG "Valeur %lld trop grande pour la colonne %s" +#define MSG_VALUE_TOO_LONG "Valeur %s trop longue pour la colonne %s de longueur %d" +#define MSG_VAL_ALLOC_ERR "Allocation impossible du noeud valeur" +#define MSG_VAL_TOO_LONG "Valeur %s trop longue pour le champ %s" +#define MSG_VIEW_ALREADY "La VIEW %s existe d‚j…" +#define MSG_VIEW_CREATED "%s view %s cr‚‚e" +#define MSG_VIEW_DROPPED "View %s supprim‚e" +#define MSG_VIEW_NOT_IN_DB "%s n'est pas une View de %s" +#define MSG_VIR_NO_DELETE "Delete impossible sur les tables %s" +#define MSG_VIR_READ_ONLY "Les tables virtuelles %s sont en lecture seulement" +#define MSG_VM_LANG "Langage au format VM, non support‚" +#define MSG_VOID_FIRST_ARG "Le premier argument ne doit pas ˆtre vide" +#define MSG_VOID_IN_STRING "Erreur: chaŒne IN vide" +#define MSG_VOID_ORDER_LIST "Liste de tri vide, erreur systŠme ?" +#define MSG_VOID_POS_DICT "Dictionnaire interne du langage vide" +#define MSG_VOID_QUERY "Requˆte vide %s" +#define MSG_WORK_AREA "Espace de travail: %s" +#define MSG_WORK_TOO_SMALL "Zone de travail trop petite, accroŒtre AreaSize" +#define MSG_WRITE_ERROR "Erreur … l'‚criture de %s" +#define MSG_WRITE_SEEK_ERR "Erreur de recherche en ‚criture: %s" +#define MSG_WRITE_STRERROR "Erreur en ‚criture sur %s: %s" +#define MSG_WRITING "Ecriture" +#define MSG_WRITING_ERROR "Erreur … l'‚criture de %s: %s" +#define MSG_WRITING_QUERY "Erreur … l'‚criture de la requˆte: " +#define MSG_WRONG_ARG_NUM "La fonction %s ne prend pas %d arguments" +#define MSG_WRONG_COL_NUM "Num‚ro de colonne %d trop grand pour %s" +#define MSG_WRONG_DB_LIST "Liste des bases de donn‚es incorrecte ou vide" +#define MSG_WRONG_FUNCTION "Mauvaise fonction %d" +#define MSG_WRONG_OP_PARM "Mauvais op‚rateur ou paramŠtres pour %s" +#define MSG_WRONG_PARMS "Mauvais paramŠtres pour %s" +#define MSG_WRONG_PASSWORD "Mot de passe ill‚gal pour %s" +#define MSG_WRONG_TYPE "type non support‚" +#define MSG_WRONG_USERFILE "La Userfile a une mauvaise taille %d" +#define MSG_WS_CONV_ERR "Erreur de convertion de %s en WS" +#define MSG_XCOL_MISMATCH "La colonne %s ne correspond pas … l'index" +#define MSG_XDB_DEL_ERROR "Erreur en supprimant des entr‚es du fichier XDB" +#define MSG_XFILE_READERR "Erreur %d en lisant le fichier index" +#define MSG_XFILE_TOO_SMALL "Le fichier index est plus petit que la taille de l'index" +#define MSG_XFILE_WRITERR "Erreur en ‚crivant le fichier index: %s" +#define MSG_XMLTAB_INIT_ERR "Erreur d'initialisation de la table XML" +#define MSG_XML_INIT_ERROR "Erreur d'initialisation du nouveau fichier XML" +#define MSG_XPATH_CNTX_ERR "Le nouveau contexte XPath ne peut ˆtre cr‚‚" +#define MSG_XPATH_EVAL_ERR "Impossible d'‚valuer l'emplacement xpath '%s'" +#define MSG_XPATH_NOT_SUPP "Xpath non support‚ colonne %s" +#define MSG_X_ARG_ADDED "%d arguments ajout‚s" +#define MSG_X_ARG_SET "%d arguments ont ‚t‚ initialis‚s" +#define MSG_X_ON_TAB " %s sur %s(" +#define MSG_ZERO_DIVIDE "Division par z‚ro dans une expression" diff --git a/storage/connect/global.h b/storage/connect/global.h new file mode 100644 index 00000000000..ae50e5ac160 --- /dev/null +++ b/storage/connect/global.h @@ -0,0 +1,253 @@ +/***********************************************************************/ +/* GLOBAL.H: Declaration file used by all CONNECT implementations. */ +/* (C) Copyright Olivier Bertrand 1993-2012 */ +/***********************************************************************/ + +/***********************************************************************/ +/* Included C-definition files common to all Plug routines */ +/***********************************************************************/ +#include <string.h> /* String manipulation declares */ +#include <stdlib.h> /* C standard library */ +#include <ctype.h> /* C language specific types */ +#include <stdio.h> /* FOPEN_MAX declaration */ +#include <time.h> /* time_t type declaration */ +#include <setjmp.h> /* Long jump declarations */ + +#if defined(WIN32) && !defined(NOEX) +#define DllExport __declspec( dllexport ) +#else // !WIN32 +#define DllExport +#endif // !WIN32 + +#if defined(DOMDOC_SUPPORT) || defined(LIBXML2_SUPPORT) +#define XML_SUPPORT 1 +#endif + +#if defined(XMSG) +// Definition used to read messages from message file. +#include "msgid.h" +#define MSG(I) PlugReadMessage(NULL, MSG_##I, #I) +#define STEP(I) PlugReadMessage(g, MSG_##I, #I) +#elif defined(NEWMSG) +// Definition used to get messages from resource. +#include "msgid.h" +#define MSG(I) PlugGetMessage(NULL, MSG_##I) +#define STEP(I) PlugGetMessage(g, MSG_##I) +#else // !XMSG and !NEWMSG +// Definition used to replace messages ID's by their definition. +#include "messages.h" +#define MSG(I) MSG_##I +#define STEP(I) MSG_##I +#endif // !XMSG and !NEWMSG + +#if defined(WIN32) +#define CRLF 2 +#else // !WIN32 +#define CRLF 1 +#endif // !WIN32 + +/***********************************************************************/ +/* Miscellaneous Constants */ +/***********************************************************************/ +#define NO_IVAL -95684275 /* Used by GetIntegerOption */ +#define VMLANG 370 /* Size of olf VM lang blocks */ +#define MAX_JUMP 24 /* Maximum jump level number */ +#define MAX_STR 1024 /* Maximum string length */ +#define STR_SIZE 501 /* Length of char strings. */ +#define STD_INPUT 0 /* Standard language input */ +#define STD_OUTPUT 1 /* Standard language output */ +#define ERROR_OUTPUT 2 /* Error message output */ +#define DEBUG_OUTPUT 3 /* Debug info output */ +#define PROMPT_OUTPUT 4 /* Prompt message output */ +#define COPY_OUTPUT 5 /* Copy of language input */ +#define STD_MSG 6 /* System message file */ +#define DEBUG_MSG 7 /* Debug message file */ +#define DUMMY 0 /* Dummy file index in Ldm block */ +#define STDIN 1 /* stdin file index in Ldm block */ +#define STDOUT 2 /* stdout file index in Ldm block */ +#define STDERR 3 /* stderr file index in Ldm block */ +#define STDEBUG 4 /* debug file index in Ldm block */ +#define STDPRN 5 /* stdprn file index in Ldm block */ +#define STDFREE 6 /* Free file index in Ldm block */ + +#define TYPE_SEM -2 /* Returned semantic function */ +#define TYPE_DFONC -2 /* Indirect sem ref in FPARM */ +#define TYPE_VOID -1 +#define TYPE_SBPAR -1 /* Phrase reference in FPARM */ +#define TYPE_SEMX 0 /* Initial semantic function type? */ +#define TYPE_ERROR 0 +#define TYPE_STRING 1 +#define TYPE_FLOAT 2 +#define TYPE_SHORT 3 +#define TYPE_TINY 4 +#define TYPE_BIGINT 5 +#define TYPE_LIST 6 +#define TYPE_INT 7 + +#if defined(OS32) + #define SYS_STAMP "OS32" +#elif defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX) + #define SYS_STAMP "UNIX" +#elif defined(OS16) + #define SYS_STAMP "OS16" +#elif defined(DOSR) + #define SYS_STAMP "DOSR" +#elif defined(WIN) + #define SYS_STAMP "WIN1" +#elif defined(WIN32) + #define SYS_STAMP "WIN2" +#else + #define SYS_STAMP "XXXX" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/***********************************************************************/ +/* Static variables */ +/***********************************************************************/ +#if defined(STORAGE) + char sys_stamp[4] = SYS_STAMP; +#else + extern char sys_stamp[]; +#endif + +/***********************************************************************/ +/* File-Selection Indicators */ +/***********************************************************************/ +#define PAT_LOG "log" + +#if defined(UNIX) || defined(LINUX) || defined(UNIV_LINUX) + /*********************************************************************/ + /* printf does not accept null pointer for %s target. */ + /*********************************************************************/ + #define SVP(S) ((S) ? S : "<null>") +#else + /*********************************************************************/ + /* printf accepts null pointer for %s target. */ + /*********************************************************************/ + #define SVP(S) S +#endif + +#if defined(STORAGE) + FILE *debug; +#else + extern FILE *debug; +#endif + + +/***********************************************************************/ +/* General purpose type definitions. */ +/***********************************************************************/ +#include "os.h" + +typedef uint OFFSET; +typedef char NAME[9]; + +typedef struct { + ushort Length; + char String[2]; + } VARSTR; + +#if !defined(PGLOBAL_DEFINED) +typedef struct _global *PGLOBAL; +#define PGLOBAL_DEFINED +#endif +typedef struct _globplg *PGS; +typedef struct _activity *PACTIVITY; +typedef struct _parm *PPARM; + +/***********************************************************************/ +/* Segment Sub-Allocation block structure declares. */ +/* Next block is an implementation dependent segment suballoc save */ +/* structure used to keep the suballocation system offsets and to */ +/* restore them if needed. This scheme implies that no SubFree be used */ +/***********************************************************************/ +typedef struct { /* Plug Area SubAlloc header */ + OFFSET To_Free; /* Offset of next free block */ + uint FreeBlk; /* Size of remaining free memory */ + } POOLHEADER, *PPOOLHEADER; + +/***********************************************************************/ +/* Language block. Containing all global information for the language */ +/* this block is saved and retrieved with the language. Information */ +/* in this block can be set and modified under Grammar editing. */ +/***********************************************************************/ +#if defined(BIT64) +typedef int TIME_T; /* Lang block size must not change */ +#else // BIT32 +typedef time_t TIME_T; /* time_t */ +#endif // BIT32 + +typedef struct { + uint Memsize; + uint Size; + } AREADEF; + +typedef struct Lang_block { + NAME LangName; /* Language name */ + NAME Application; /* Application name */ + } LANG, *PLANG; + +/***********************************************************************/ +/* Application block. It contains all global information for the */ +/* current parse and execution using the corresponding language. */ +/* This block is dynamically allocated and set at language init. */ +/***********************************************************************/ +typedef struct _activity { /* Describes activity and language */ + void *Aptr; /* Points to user work area(s) */ + NAME Ap_Name; /* Current application name */ + } ACTIVITY; + +/*---------------- UNIT ?????????? VERSION ? ----------------------*/ +typedef struct _parm { + void *Value; + short Type, Domain; + PPARM Next; + } PARM; + +/***********************************************************************/ +/* Global Structure Block. This block contains, or points to, all */ +/* information used by CONNECT tables. Passed as an argument */ +/* to any routine allows it to have access to the entire information */ +/* currently available for the whole set of loaded languages. */ +/***********************************************************************/ +typedef struct _global { /* Global structure */ + void *Sarea; /* Points to work area */ + uint Sarea_Size; /* Work area size */ + PACTIVITY Activityp, ActivityStart; + char Message[MAX_STR]; + short Trace; + int Createas; /* To pass info to created table */ + void *Xchk; /* indexes in create/alter */ + int jump_level; + jmp_buf jumper[MAX_JUMP + 2]; + } GLOBAL; + +/***********************************************************************/ +/* Exported routine declarations. */ +/***********************************************************************/ +#if defined(XMSG) +DllExport char *PlugReadMessage(PGLOBAL, int, char *); +#elif defined(NEWMSG) +DllExport char *PlugGetMessage(PGLOBAL, int); +#endif // XMSG || NEWMSG +#if defined(WIN32) +DllExport short GetLineLength(PGLOBAL); // Console line length +#endif // WIN32 +DllExport PGLOBAL PlugInit(LPCSTR, uint); // Plug global initialization +DllExport int PlugExit(PGLOBAL); // Plug global termination +DllExport LPSTR PlugRemoveType(LPSTR, LPCSTR); +DllExport LPCSTR PlugSetPath(LPSTR, LPCSTR, LPCSTR); +DllExport void *PlugAllocMem(PGLOBAL, uint); +DllExport BOOL PlugSubSet(PGLOBAL, void *, uint); +DllExport void *PlugSubAlloc(PGLOBAL, void *, size_t); +DllExport void *MakePtr(void *, OFFSET); +DllExport void htrc(char const *fmt, ...); + +#if defined(__cplusplus) +} // extern "C" +#endif + +/*-------------------------- End of Global.H --------------------------*/ diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc new file mode 100644 index 00000000000..5ce13e446f0 --- /dev/null +++ b/storage/connect/ha_connect.cc @@ -0,0 +1,4447 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file ha_connect.cc + + @brief + The ha_connect engine is a stubbed storage engine that enables to create tables + based on external data. Principally they are based on plain files of many + different types, but also on collections of such files, collection of tables, + ODBC tables retrieving data from other DBMS having an ODBC server, and even + virtual tables. + + @details + ha_connect will let you create/open/delete tables, the created table can be + done specifying an already existing file, the drop table command will just + suppress the table definition but not the eventual data file. + Indexes are not yet supported but data can be inserted, updated or deleted. + + You can enable the CONNECT storage engine in your build by doing the + following during your build process:<br> ./configure + --with-connect-storage-engine (not implemented yet) + + You can install the CONNECT handler as all other storage handlers. + + Once this is done, MySQL will let you create tables with:<br> + CREATE TABLE <table name> (...) ENGINE=CONNECT; + + The example storage engine does not use table locks. It + implements an example "SHARE" that is inserted into a hash by table + name. This is not used yet. + + Please read the object definition in ha_connect.h before reading the rest + of this file. + + @note + This MariaDB CONNECT handler is currently an adaptation of the XDB handler + that was written for MySQL version 4.1.2-alpha. Its overall design should + be enhanced in the future to meet MariaDB requirements. + + @note + It was written also from the Brian's ha_example handler and contains parts + of it that are there but not currently used, such as table variables. + + @note + When you create an CONNECT table, the MySQL Server creates a table .frm + (format) file in the database directory, using the table name as the file + name as is customary with MySQL. No other files are created. To get an idea + of what occurs, here is an example select that would do a scan of an entire + table: + + @code + ha-connect::open + ha_connect::store_lock + ha_connect::external_lock + ha_connect::info + ha_connect::rnd_init + ha_connect::extra + ENUM HA_EXTRA_CACHE Cache record in HA_rrnd() + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::rnd_next + ha_connect::extra + ENUM HA_EXTRA_NO_CACHE End caching of records (def) + ha_connect::external_lock + ha_connect::extra + ENUM HA_EXTRA_RESET Reset database to after open + @endcode + + Here you see that the connect storage engine has 9 rows called before + rnd_next signals that it has reached the end of its data. Calls to + ha_connect::extra() are hints as to what will be occuring to the request. + + Happy use!<br> + -Olivier +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#define MYSQL_SERVER 1 +#define DONT_DEFINE_VOID +//#include "sql_partition.h" +#include "sql_class.h" +#include "create_options.h" +#include "mysql_com.h" +#include "field.h" +#include "sql_parse.h" +#include "sql_base.h" +#undef OFFSET + +#define NOPARSE +#if defined(UNIX) +#include "osutil.h" +#endif // UNIX +#include "global.h" +#include "plgdbsem.h" +#if defined(ODBC_SUPPORT) +#include "odbccat.h" +#endif // ODBC_SUPPORT +#if defined(MYSQL_SUPPORT) +#include "xtable.h" +#include "tabmysql.h" +#endif // MYSQL_SUPPORT +#include "filamdbf.h" +#include "tabfmt.h" +#include "reldef.h" +#include "tabcol.h" +#include "xindex.h" +#if defined(WIN32) +#include <io.h> +#include "tabwmi.h" +#endif // WIN32 +#include "connect.h" +#include "user_connect.h" +#include "ha_connect.h" +#include "mycat.h" +#include "myutil.h" +#include "preparse.h" + +#define PLGXINI "plgcnx.ini" /* Configuration settings file */ +#define my_strupr(p) my_caseup_str(default_charset_info, (p)); +#define my_strlwr(p) my_casedn_str(default_charset_info, (p)); +#define my_stricmp(a,b) my_strcasecmp(default_charset_info, (a), (b)) + +#ifdef LIBXML2_SUPPORT +void XmlInitParserLib(void); +void XmlCleanupParserLib(void); +#endif // LIBXML2_SUPPORT + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +extern "C" char plgxini[]; +extern "C" char plgini[]; +extern "C" char nmfile[]; +extern "C" char pdebug[]; + +extern "C" { + char version[]= "Version 1.01.0004 April 10, 2013"; + +#if defined(XMSG) + char msglang[]; // Default message language +#endif + int trace= 0; // The general trace value +} // extern "C" + +/****************************************************************************/ +/* Initialize the ha_connect static members. */ +/****************************************************************************/ +#define CONNECT_INI "connect.ini" +char connectini[_MAX_PATH]= CONNECT_INI; +int xtrace= 0; +ulong ha_connect::num= 0; +//int DTVAL::Shift= 0; + +static handler *connect_create_handler(handlerton *hton, + TABLE_SHARE *table, + MEM_ROOT *mem_root); + +handlerton *connect_hton; + +/* Variables for connect share methods */ + +/* + Hash used to track the number of open tables; variable for connect share + methods +*/ +static HASH connect_open_tables; + +/* The mutex used to init the hash; variable for example share methods */ +mysql_mutex_t connect_mutex; + + +/** + structure for CREATE TABLE options (table options) + + These can be specified in the CREATE TABLE: + CREATE TABLE ( ... ) {...here...} +*/ +struct ha_table_option_struct { + const char *type; + const char *filename; + const char *optname; + const char *tabname; + const char *tablist; + const char *dbname; + const char *separator; +//const char *connect; + const char *qchar; + const char *module; + const char *subtype; + const char *catfunc; + const char *oplist; + const char *data_charset; + int lrecl; + int elements; +//int estimate; + int multiple; + int header; + int quoted; + int ending; + int compressed; + bool mapped; + bool huge; + bool split; + bool readonly; + bool sepindex; + }; + +#if defined(MARIADB) +ha_create_table_option connect_table_option_list[]= +{ + // These option are for stand alone Connect tables + HA_TOPTION_STRING("TABLE_TYPE", type), + HA_TOPTION_STRING("FILE_NAME", filename), + HA_TOPTION_STRING("XFILE_NAME", optname), +//HA_TOPTION_STRING("CONNECT_STRING", connect), + HA_TOPTION_STRING("TABNAME", tabname), + HA_TOPTION_STRING("TABLE_LIST", tablist), + HA_TOPTION_STRING("DBNAME", dbname), + HA_TOPTION_STRING("SEP_CHAR", separator), + HA_TOPTION_STRING("QCHAR", qchar), + HA_TOPTION_STRING("MODULE", module), + HA_TOPTION_STRING("SUBTYPE", subtype), + HA_TOPTION_STRING("CATFUNC", catfunc), + HA_TOPTION_STRING("OPTION_LIST", oplist), + HA_TOPTION_STRING("DATA_CHARSET", data_charset), + HA_TOPTION_NUMBER("LRECL", lrecl, 0, 0, INT_MAX32, 1), + HA_TOPTION_NUMBER("BLOCK_SIZE", elements, 0, 0, INT_MAX32, 1), +//HA_TOPTION_NUMBER("ESTIMATE", estimate, 0, 0, INT_MAX32, 1), + HA_TOPTION_NUMBER("MULTIPLE", multiple, 0, 0, 2, 1), + HA_TOPTION_NUMBER("HEADER", header, 0, 0, 3, 1), + HA_TOPTION_NUMBER("QUOTED", quoted, -1, 0, 3, 1), + HA_TOPTION_NUMBER("ENDING", ending, -1, 0, INT_MAX32, 1), + HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1), +//HA_TOPTION_BOOL("COMPRESS", compressed, 0), + HA_TOPTION_BOOL("MAPPED", mapped, 0), + HA_TOPTION_BOOL("HUGE", huge, 0), + HA_TOPTION_BOOL("SPLIT", split, 0), + HA_TOPTION_BOOL("READONLY", readonly, 0), + HA_TOPTION_BOOL("SEPINDEX", sepindex, 0), + HA_TOPTION_END +}; +#endif // MARIADB + + +/** + structure for CREATE TABLE options (field options) + + These can be specified in the CREATE TABLE per field: + CREATE TABLE ( field ... {...here...}, ... ) +*/ +struct ha_field_option_struct +{ + int offset; + int freq; // Not used by this version + int opt; // Not used by this version + int fldlen; + const char *dateformat; + const char *fieldformat; + char *special; +}; + +#if defined(MARIADB) +ha_create_table_option connect_field_option_list[]= +{ + HA_FOPTION_NUMBER("FLAG", offset, -1, 0, INT_MAX32, 1), + HA_FOPTION_NUMBER("FREQUENCY", freq, 0, 0, INT_MAX32, 1), // not used + HA_FOPTION_NUMBER("OPT_VALUE", opt, 0, 0, 2, 1), // used for indexing + HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1), + HA_FOPTION_STRING("DATE_FORMAT", dateformat), + HA_FOPTION_STRING("FIELD_FORMAT", fieldformat), + HA_FOPTION_STRING("SPECIAL", special), + HA_FOPTION_END +}; +#endif // MARIADB + +/***********************************************************************/ +/* Push G->Message as a MySQL warning. */ +/***********************************************************************/ +bool PushWarning(PGLOBAL g, PTDBASE tdbp) + { + PHC phc; + THD *thd; + MYCAT *cat= (MYCAT*)tdbp->GetDef()->GetCat(); + + if (!cat || !(phc= cat->GetHandler()) || !phc->GetTable() || + !(thd= (phc->GetTable())->in_use)) + return true; + + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + return false; + } // end of PushWarning + +/** + @brief + Function we use in the creation of our hash to get key. +*/ +static uchar* connect_get_key(CONNECT_SHARE *share, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length=share->table_name_length; + return (uchar*) share->table_name; +} + +#ifdef HAVE_PSI_INTERFACE +static PSI_mutex_key con_key_mutex_connect, con_key_mutex_CONNECT_SHARE_mutex; + +static PSI_mutex_info all_connect_mutexes[]= +{ + { &con_key_mutex_connect, "connect", PSI_FLAG_GLOBAL}, + { &con_key_mutex_CONNECT_SHARE_mutex, "CONNECT_SHARE::mutex", 0} +}; + +static void init_connect_psi_keys() +{ + const char* category= "connect"; + int count; + + if (PSI_server == NULL) + return; + + count= array_elements(all_connect_mutexes); + PSI_server->register_mutex(category, all_connect_mutexes, count); +} +#endif + + +/** + @brief + Plugin initialization +*/ +static int connect_init_func(void *p) +{ + DBUG_ENTER("connect_init_func"); + char dir[_MAX_PATH - sizeof(CONNECT_INI) - 1]; + +#ifdef LIBXML2_SUPPORT + XmlInitParserLib(); +#endif // LIBXML2_SUPPORT + + /* Build connect.ini file name */ + my_getwd(dir, sizeof(dir) - 1, MYF(0)); + snprintf(connectini, sizeof(connectini), "%s%s", dir, CONNECT_INI); + sql_print_information("CONNECT: %s=%s", CONNECT_INI, connectini); + + if ((xtrace= GetPrivateProfileInt("CONNECT", "Trace", 0, connectini))) + { + sql_print_information("CONNECT: xtrace=%d", xtrace); + sql_print_information("CONNECT: plgini=%s", plgini); + sql_print_information("CONNECT: plgxini=%s", plgxini); + sql_print_information("CONNECT: nmfile=%s", nmfile); + sql_print_information("CONNECT: pdebug=%s", pdebug); + sql_print_information("CONNECT: version=%s", version); + trace= xtrace; + } // endif xtrace + + +#ifdef HAVE_PSI_INTERFACE + init_connect_psi_keys(); +#endif + + connect_hton= (handlerton *)p; + mysql_mutex_init(con_key_mutex_connect, &connect_mutex, MY_MUTEX_INIT_FAST); +//VOID(mysql_mutex_init(&connect_mutex, MY_MUTEX_INIT_FAST)); + (void) my_hash_init(&connect_open_tables, system_charset_info, 32, 0, 0, + (my_hash_get_key) connect_get_key, 0, 0); + +//connect_hton->name= "CONNECT"; + connect_hton->state= SHOW_OPTION_YES; +//connect_hton->comment= "CONNECT handler"; + connect_hton->create= connect_create_handler; + connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_NO_PARTITION; +#if defined(MARIADB) + connect_hton->db_type= DB_TYPE_AUTOASSIGN; + connect_hton->table_options= connect_table_option_list; + connect_hton->field_options= connect_field_option_list; +#else // !MARIADB +//connect_hton->system_database= connect_system_database; +//connect_hton->is_supported_system_table= connect_is_supported_system_table; +#endif // !MARIADB + + if (xtrace) + sql_print_information("connect_init: hton=%p", p); + + DTVAL::SetTimeShift(); // Initialize time zone shift once for all + DBUG_RETURN(0); +} + + +/** + @brief + Plugin clean up +*/ +static int connect_done_func(void *p) +{ + int error= 0; + PCONNECT pc, pn; + DBUG_ENTER("connect_done_func"); + +#ifdef LIBXML2_SUPPORT + XmlCleanupParserLib(); +#endif // LIBXML2_SUPPORT + + if (connect_open_tables.records) + error= 1; + + for (pc= user_connect::to_users; pc; pc= pn) { + if (pc->g) + PlugCleanup(pc->g, true); + + pn= pc->next; + delete pc; + } // endfor pc + + my_hash_free(&connect_open_tables); + mysql_mutex_destroy(&connect_mutex); + + DBUG_RETURN(error); +} + + +/** + @brief + Example of simple lock controls. The "share" it creates is a + structure we will pass to each example handler. Do you have to have + one of these? Well, you have pieces that are used for locking, and + they are needed to function. +*/ + +static CONNECT_SHARE *get_share(const char *table_name, TABLE *table) +{ + CONNECT_SHARE *share; + uint length; + char *tmp_name; + + mysql_mutex_lock(&connect_mutex); + length=(uint) strlen(table_name); + + if (!(share=(CONNECT_SHARE*)my_hash_search(&connect_open_tables, + (uchar*) table_name, length))) { + if (!(share=(CONNECT_SHARE *)my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &share, sizeof(*share), &tmp_name, length+1, NullS))) { + mysql_mutex_unlock(&connect_mutex); + return NULL; + } // endif share + + share->use_count=0; + share->table_name_length=length; + share->table_name=tmp_name; + strmov(share->table_name, table_name); + + if (my_hash_insert(&connect_open_tables, (uchar*) share)) + goto error; + + thr_lock_init(&share->lock); + mysql_mutex_init(con_key_mutex_CONNECT_SHARE_mutex, + &share->mutex, MY_MUTEX_INIT_FAST); + } // endif share + + share->use_count++; + mysql_mutex_unlock(&connect_mutex); + return share; + +error: + mysql_mutex_destroy(&share->mutex); + my_free(share); + return NULL; +} + + +/** + @brief + Free lock controls. We call this whenever we close a table. If the table had + the last reference to the share, then we free memory associated with it. +*/ + +static int free_share(CONNECT_SHARE *share) +{ + mysql_mutex_lock(&connect_mutex); + + if (!--share->use_count) { + my_hash_delete(&connect_open_tables, (uchar*) share); + thr_lock_delete(&share->lock); + mysql_mutex_destroy(&share->mutex); +#if !defined(MARIADB) + my_free(share->table_options); + my_free(share->field_options); +#endif // !MARIADB + my_free(share); + } // endif share + + mysql_mutex_unlock(&connect_mutex); + return 0; +} + +static handler* connect_create_handler(handlerton *hton, + TABLE_SHARE *table, + MEM_ROOT *mem_root) +{ + handler *h= new (mem_root) ha_connect(hton, table); + + if (xtrace) + printf("New CONNECT %p, table: %s\n", + h, table ? table->table_name.str : "<null>"); + + return h; +} // end of connect_create_handler + +/****************************************************************************/ +/* ha_connect constructor. */ +/****************************************************************************/ +ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg) + :handler(hton, table_arg) +{ + hnum= ++num; + xp= NULL; // Tested in next call + xp= (table) ? GetUser(table->in_use) : NULL; + tdbp= NULL; + sdvalin= NULL; + sdvalout= NULL; + xmod= MODE_ANY; + istable= false; +//*tname= '\0'; + bzero((char*) &xinfo, sizeof(XINFO)); + valid_info= false; + valid_query_id= 0; + creat_query_id= (table && table->in_use) ? table->in_use->query_id : 0; + stop= false; + indexing= -1; + data_file_name= NULL; + index_file_name= NULL; + enable_activate_all_index= 0; + int_table_flags= (HA_NO_TRANSACTIONS | HA_NO_PREFIX_CHAR_KEYS); + ref_length= sizeof(int); + share= NULL; +#if !defined(MARIADB) + table_options= NULL; + field_options= NULL; +#endif // !MARIADB + tshp= NULL; +} // end of ha_connect constructor + + +/****************************************************************************/ +/* ha_connect destructor. */ +/****************************************************************************/ +ha_connect::~ha_connect(void) +{ + if (xp) { + PCONNECT p; + + xp->count--; + + for (p= user_connect::to_users; p; p= p->next) + if (p == xp) + break; + + if (p && !p->count) { + if (p->next) + p->next->previous= p->previous; + + if (p->previous) + p->previous->next= p->next; + else + user_connect::to_users= p->next; + + } // endif p + + if (!xp->count) { + PlugCleanup(xp->g, true); + delete xp; + } // endif count + + } // endif xp + +#if !defined(MARIADB) + my_free(table_options); + my_free(field_options); +#endif // !MARIADB +} // end of ha_connect destructor + + +/****************************************************************************/ +/* Get a pointer to the user of this handler. */ +/****************************************************************************/ +PCONNECT ha_connect::GetUser(THD *thd) +{ + const char *dbn= NULL; + + if (!thd) + return NULL; + + if (xp && thd == xp->thdp) + return xp; + + for (xp= user_connect::to_users; xp; xp= xp->next) + if (thd == xp->thdp) + break; + + if (!xp) { + xp= new user_connect(thd, dbn); + + if (xp->user_init(this)) { + delete xp; + xp= NULL; + } // endif user_init + + } else + xp->count++; + + return xp; +} // end of GetUser + + +/****************************************************************************/ +/* Get the global pointer of the user of this handler. */ +/****************************************************************************/ +PGLOBAL ha_connect::GetPlug(THD *thd) +{ + PCONNECT lxp= GetUser(thd); + return (lxp) ? lxp->g : NULL; +} // end of GetPlug + + +/****************************************************************************/ +/* Return the value of an option specified in the option list. */ +/****************************************************************************/ +char *ha_connect::GetListOption(const char *opname, + const char *oplist, + const char *def) +{ + char key[16], val[256]; + char *pk, *pv, *pn; + char *opval= (char*) def; + int n; + + for (pk= (char*)oplist; pk; pk= ++pn) { + pn= strchr(pk, ','); + pv= strchr(pk, '='); + + if (pv && (!pn || pv < pn)) { + n= pv - pk; + memcpy(key, pk, n); + key[n]= 0; + pv++; + + if (pn) { + n= pn - pv; + memcpy(val, pv, n); + val[n]= 0; + } else + strcpy(val, pv); + + } else { + if (pn) { + n= pn - pk; + memcpy(key, pk, n); + key[n]= 0; + } else + strcpy(key, pk); + + val[0]= 0; + } // endif pv + + if (!stricmp(opname, key)) { + opval= (char*)PlugSubAlloc(xp->g, NULL, strlen(val) + 1); + strcpy(opval, val); + break; + } else if (!pn) + break; + + } // endfor pk + + return opval; +} // end of GetListOption + +/****************************************************************************/ +/* Return the table option structure. */ +/****************************************************************************/ +PTOS ha_connect::GetTableOptionStruct(TABLE *tab) +{ +#if defined(MARIADB) + return (tshp) ? tshp->option_struct : tab->s->option_struct; +#else // !MARIADB + if (share && share->table_options) + return share->table_options; + else if (table_options) + return table_options; + + char *pk, *pv, *pn, *val; + size_t len= sizeof(ha_table_option_struct) + tab->s->comment.length + 1; + PTOS top= (PTOS)my_malloc(len, MYF(MY_FAE | MY_ZEROFILL)); + + top->quoted= -1; // Default value + top->ending= -1; // Default value + pk= (char *)top + sizeof(ha_table_option_struct); + memcpy(pk, tab->s->comment.str, tab->s->comment.length); + + for (; pk; pk= ++pn) { + pn= strchr(pk, ','); + pv= strchr(pk, '='); + + if (pn) *pn= 0; + + if (pv) *pv= 0; + + val= (pv && (!pn || pv < pn)) ? pv + 1 : ""; + + if (!stricmp(pk, "type") || !stricmp(pk, "Table_Type")) { + top->type= val; + } else if (!stricmp(pk, "fn") || !stricmp(pk, "filename") + || !stricmp(pk, "File_Name")) { + top->filename= val; + } else if (!stricmp(pk, "optfn") || !stricmp(pk, "optname") + || !stricmp(pk, "Xfile_Name")) { + top->optname= val; + } else if (!stricmp(pk, "name") || !stricmp(pk, "tabname")) { + top->tabname= val; + } else if (!stricmp(pk, "tablist") || !stricmp(pk, "tablelist") + || !stricmp(pk, "Table_list")) { + top->tablist= val; + } else if (!stricmp(pk, "sep") || !stricmp(pk, "separator") + || !stricmp(pk, "Sep_Char")) { + top->separator= val; + } else if (!stricmp(pk, "db") || !stricmp(pk, "DBName") + || !stricmp(pk, "Database") { + top->dbname= val; + } else if (!stricmp(pk, "qchar")) { + top->qchar= val; + } else if (!stricmp(pk, "module")) { + top->module= val; + } else if (!stricmp(pk, "subtype")) { + top->subtype= val; + } else if (!stricmp(pk, "lrecl")) { + top->lrecl= atoi(val); + } else if (!stricmp(pk, "elements")) { + top->elements= atoi(val); + } else if (!stricmp(pk, "multiple")) { + top->multiple= atoi(val); + } else if (!stricmp(pk, "header")) { + top->header= atoi(val); + } else if (!stricmp(pk, "quoted")) { + top->quoted= atoi(val); + } else if (!stricmp(pk, "ending")) { + top->ending= atoi(val); + } else if (!stricmp(pk, "compressed")) { + top->compressed= atoi(val); + } else if (!stricmp(pk, "mapped")) { + top->mapped= (!*val || *val == 'y' || *val == 'Y' || atoi(val) != 0); + } else if (!stricmp(pk, "huge")) { + top->huge= (!*val || *val == 'y' || *val == 'Y' || atoi(val) != 0); + } else if (!stricmp(pk, "split")) { + top->split= (!*val || *val == 'y' || *val == 'Y' || atoi(val) != 0); + } else if (!stricmp(pk, "readonly") || !stricmp(pk, "protected")) { + top->readonly= (!*val || *val == 'y' || *val == 'Y' || atoi(val) != 0); + } // endif's + + if (!pn) + break; + + } // endfor pk + + // This to get all other options + top->oplist= tab->s->comment.str; + + if (share) + share->table_options= top; + else + table_options= top; + + return top; +#endif // !MARIADB +} // end of GetTableOptionStruct + +/****************************************************************************/ +/* Return the value of a string option or NULL if not specified. */ +/****************************************************************************/ +char *ha_connect::GetStringOption(char *opname, char *sdef) +{ + char *opval= NULL; + PTOS options= GetTableOptionStruct(table); + + if (!options) + ; + else if (!stricmp(opname, "Type")) + opval= (char*)options->type; + else if (!stricmp(opname, "Filename")) + opval= (char*)options->filename; + else if (!stricmp(opname, "Optname")) + opval= (char*)options->optname; + else if (!stricmp(opname, "Tabname")) + opval= (char*)options->tabname; + else if (!stricmp(opname, "Tablist")) + opval= (char*)options->tablist; + else if (!stricmp(opname, "Database") || + !stricmp(opname, "DBname")) + opval= (char*)options->dbname; + else if (!stricmp(opname, "Separator")) + opval= (char*)options->separator; + else if (!stricmp(opname, "Connect")) +// opval= (char*)options->connect; + opval= table->s->connect_string.str; + else if (!stricmp(opname, "Qchar")) + opval= (char*)options->qchar; + else if (!stricmp(opname, "Module")) + opval= (char*)options->module; + else if (!stricmp(opname, "Subtype")) + opval= (char*)options->subtype; + else if (!stricmp(opname, "Catfunc")) + opval= (char*)options->catfunc; + else if (!stricmp(opname, "Data_charset")) + opval= (char*)options->data_charset; + + if (!opval && options && options->oplist) + opval= GetListOption(opname, options->oplist); + + if (!opval) { + if (sdef && !strcmp(sdef, "*")) { + // Return the handler default value + if (!stricmp(opname, "Dbname") || !stricmp(opname, "Database")) + opval= (char*)GetDBName(NULL); // Current database + else + opval= sdef; // Caller default + + } else + opval= sdef; // Caller default + + } // endif !opval + + return opval; +} // end of GetStringOption + +/****************************************************************************/ +/* Return the value of a Boolean option or bdef if not specified. */ +/****************************************************************************/ +bool ha_connect::GetBooleanOption(char *opname, bool bdef) +{ + bool opval= bdef; + char *pv; + PTOS options= GetTableOptionStruct(table); + + if (!options) + ; + else if (!stricmp(opname, "Mapped")) + opval= options->mapped; + else if (!stricmp(opname, "Huge")) + opval= options->huge; +//else if (!stricmp(opname, "Compressed")) +// opval= options->compressed; + else if (!stricmp(opname, "Split")) + opval= options->split; + else if (!stricmp(opname, "Readonly")) + opval= options->readonly; + else if (!stricmp(opname, "SepIndex")) + opval= options->sepindex; + else if (options->oplist) + if ((pv= GetListOption(opname, options->oplist))) + opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0); + + return opval; +} // end of GetBooleanOption + +/****************************************************************************/ +/* Set the value of the opname option (does not work for oplist options) */ +/* Currently used only to set the Sepindex value. */ +/****************************************************************************/ +bool ha_connect::SetBooleanOption(char *opname, bool b) +{ + PTOS options= GetTableOptionStruct(table); + + if (!options) + return true; + + if (!stricmp(opname, "SepIndex")) + options->sepindex= b; + else + return true; + + return false; +} // end of SetBooleanOption + +/****************************************************************************/ +/* Return the value of an integer option or NO_IVAL if not specified. */ +/****************************************************************************/ +int ha_connect::GetIntegerOption(char *opname) +{ + int opval= NO_IVAL; + char *pv; + PTOS options= GetTableOptionStruct(table); + + if (!options) + ; + else if (!stricmp(opname, "Lrecl")) + opval= options->lrecl; + else if (!stricmp(opname, "Elements")) + opval= options->elements; + else if (!stricmp(opname, "Estimate")) +// opval= options->estimate; + opval= (int)table->s->max_rows; + else if (!stricmp(opname, "Avglen")) + opval= (int)table->s->avg_row_length; + else if (!stricmp(opname, "Multiple")) + opval= options->multiple; + else if (!stricmp(opname, "Header")) + opval= options->header; + else if (!stricmp(opname, "Quoted")) + opval= options->quoted; + else if (!stricmp(opname, "Ending")) + opval= options->ending; + else if (!stricmp(opname, "Compressed")) + opval= (options->compressed); + + if (opval == NO_IVAL && options && options->oplist) + if ((pv= GetListOption(opname, options->oplist))) + opval= atoi(pv); + + return opval; +} // end of GetIntegerOption + +/****************************************************************************/ +/* Set the value of the opname option (does not work for oplist options) */ +/* Currently used only to set the Lrecl value. */ +/****************************************************************************/ +bool ha_connect::SetIntegerOption(char *opname, int n) +{ + PTOS options= GetTableOptionStruct(table); + + if (!options) + return true; + + if (!stricmp(opname, "Lrecl")) + options->lrecl= n; + else if (!stricmp(opname, "Elements")) + options->elements= n; +//else if (!stricmp(opname, "Estimate")) +// options->estimate= n; + else if (!stricmp(opname, "Multiple")) + options->multiple= n; + else if (!stricmp(opname, "Header")) + options->header= n; + else if (!stricmp(opname, "Quoted")) + options->quoted= n; + else if (!stricmp(opname, "Ending")) + options->ending= n; + else if (!stricmp(opname, "Compressed")) + options->compressed= n; + else + return true; +//else if (options->oplist) +// SetListOption(opname, options->oplist, n); + + return false; +} // end of SetIntegerOption + +/****************************************************************************/ +/* Return a field option structure. */ +/****************************************************************************/ +PFOS ha_connect::GetFieldOptionStruct(Field *fdp) +{ +#if defined(MARIADB) + return fdp->option_struct; +#else // !MARIADB + if (share && share->field_options) + return &share->field_options[fdp->field_index]; + else if (field_options) + return &field_options[fdp->field_index]; + + char *pc, *pk, *pv, *pn, *val; + int i, k, n= table->s->fields; + size_t len= n + n * sizeof(ha_field_option_struct); + PFOS fp, fop; + + for (i= 0; i < n; i++) + len+= table->s->field[i]->comment.length; + + fop= (PFOS)my_malloc(len, MYF(MY_FAE | MY_ZEROFILL)); + pc= (char*)fop + n * sizeof(ha_field_option_struct); + + for (i= k= 0; i < n; i++) { + fp= &fop[i]; + fp->offset= -1; // Default value + + if (!table->s->field[i]->comment.length) + continue; + + memcpy(pc, table->s->field[i]->comment.str, + table->s->field[i]->comment.length); + + for (pk= pc; pk; pk= ++pn) { + if ((pn= strchr(pk, ','))) *pn= 0; + if ((pv= strchr(pk, '='))) *pv= 0; + val= (pv && (!pn || pv < pn)) ? pv + 1 : ""; + + if (!stricmp(pk, "datefmt") || !stricmp(pk, "date_format")) { + fp->dateformat= val; + } else if (!stricmp(pk, "fieldfmt") || !stricmp(pk, "field_format")) { + fp->fieldformat= val; + } else if (!stricmp(pk, "special")) { + fp->special= val; + } else if (!stricmp(pk, "offset") || !stricmp(pk, "flag")) { + fp->offset= atoi(val); + } else if (!stricmp(pk, "freq")) { + fp->freq= atoi(val); + } else if (!stricmp(pk, "opt")) { + fp->opt= atoi(val); + } else if (!stricmp(pk, "fldlen") || !stricmp(pk, "field_length")) { + fp->fldlen= atoi(val); + } // endif's + + if (!pn) + break; + + } // endfor pk + + pc+= table->s->field[i]->comment.length + 1; + } // endfor i + + if (share) + share->field_options= fop; + else + field_options= fop; + + return &fop[fdp->field_index]; +#endif // !MARIADB +} // end of GetFildOptionStruct + +/****************************************************************************/ +/* Returns the column description structure used to make the column. */ +/****************************************************************************/ +void *ha_connect::GetColumnOption(void *field, PCOLINFO pcf) +{ + const char *cp; + ha_field_option_struct *fop; + Field* fp; + Field* *fldp; + + // Double test to be on the safe side + if (!table) + return NULL; + + // Find the column to describe + if (field) { + fldp= (Field**)field; + fldp++; + } else + fldp= (tshp) ? tshp->field : table->field; + + if (!(fp= *fldp)) + return NULL; + + // Get the CONNECT field options structure + fop= GetFieldOptionStruct(fp); + pcf->Flags= 0; + + // Now get column information + if (fop && fop->special) { + pcf->Name= "*"; + return fldp; + } else + pcf->Name= (char*)fp->field_name; + + pcf->Prec= 0; + pcf->Opt= (fop) ? fop->opt : 0; + + if ((pcf->Length= fp->field_length) < 0) + pcf->Length= 256; // BLOB? + + if (fop) { + pcf->Offset= fop->offset; +// pcf->Freq= fop->freq; + pcf->Datefmt= (char*)fop->dateformat; + pcf->Fieldfmt= (char*)fop->fieldformat; + } else { + pcf->Offset= -1; +// pcf->Freq= 0; + pcf->Datefmt= NULL; + pcf->Fieldfmt= NULL; + } // endif fop + + switch (fp->type()) { + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: + pcf->Flags |= U_VAR; + case MYSQL_TYPE_STRING: + pcf->Type= TYPE_STRING; + + // Do something for case + cp= fp->charset()->name; + + // Find if collation name ends by _ci + if (!strcmp(cp + strlen(cp) - 3, "_ci")) { + pcf->Prec= 1; // Case insensitive + pcf->Opt= 0; // Prevent index opt until it is safe + } // endif ci + + break; + case MYSQL_TYPE_LONG: + pcf->Type= TYPE_INT; + break; + case MYSQL_TYPE_SHORT: + pcf->Type= TYPE_SHORT; + break; + case MYSQL_TYPE_TINY: + pcf->Type= TYPE_TINY; + break; + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_FLOAT: + pcf->Type= TYPE_FLOAT; + pcf->Prec= max(min(fp->decimals(), ((unsigned)pcf->Length - 2)), 0); + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + pcf->Type= TYPE_DATE; + + // Field_length is only used for DATE columns + if (fop->fldlen) + pcf->Length= fop->fldlen; + else { + int len; + + if (pcf->Datefmt) { + // Find the (max) length produced by the date format + char buf[256]; + PGLOBAL g= GetPlug(table->in_use); +#if defined(WIN32) + struct tm datm= {0,0,0,12,11,112,0,0,0}; +#else // !WIN32 + struct tm datm= {0,0,0,12,11,112,0,0,0,0,0}; +#endif // !WIN32 + PDTP pdtp= MakeDateFormat(g, pcf->Datefmt, false, true, 0); + + len= strftime(buf, 256, pdtp->OutFmt, &datm); + } else + len= 0; + + // 11 is for signed numeric representation of the date + pcf->Length= (len) ? len : 11; + } // endelse + + break; + case MYSQL_TYPE_LONGLONG: + pcf->Type= TYPE_BIGINT; + break; + default: + pcf->Type=TYPE_ERROR; + } // endswitch type + + // This is used to skip null bit + if (fp->real_maybe_null()) + pcf->Flags |= U_NULLS; + +#if defined(MARIADB) + // Mark virtual columns as such + if (fp->vcol_info && !fp->stored_in_db) + pcf->Flags |= U_VIRTUAL; +#endif // MARIADB + + pcf->Key= 0; // Not used when called from MySQL + pcf->Remark= fp->comment.str; + return fldp; +} // end of GetColumnOption + +/****************************************************************************/ +/* Returns the index description structure used to make the index. */ +/****************************************************************************/ +PIXDEF ha_connect::GetIndexInfo(void) +{ + char *name, *pn; + bool unique; + PIXDEF xdp, pxd=NULL, toidx= NULL; + PKPDEF kpp, pkp; + PGLOBAL& g= xp->g; + KEY kp; + + for (int n= 0; (unsigned)n < table->s->keynames.count; n++) { + if (xtrace) + printf("Getting created index %d info\n", n + 1); + + // Find the index to describe + kp= table->s->key_info[n]; + + // Now get index information + pn= (char*)table->s->keynames.type_names[n]; + name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1); + strcpy(name, pn); // This is probably unuseful + unique= (kp.flags & 1) != 0; + pkp= NULL; + + // Allocate the index description block + xdp= new(g) INDEXDEF(name, unique, n); + + // Get the the key parts info + for (int k= 0; (unsigned)k < kp.key_parts; k++) { + pn= (char*)kp.key_part[k].field->field_name; + name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1); + strcpy(name, pn); // This is probably unuseful + + // Allocate the key part description block + kpp= new(g) KPARTDEF(name, k + 1); + kpp->SetKlen(kp.key_part[k].length); + +#if 0 // NIY + // Index on auto increment column can be an XXROW index + if (kp.key_part[k].field->flags & AUTO_INCREMENT_FLAG && + kp.key_parts == 1) { + char *type= GetStringOption("Type", "DOS"); + TABTYPE typ= GetTypeID(type); + + xdp->SetAuto(IsTypeFixed(typ)); + } // endif AUTO_INCREMENT +#endif // 0 + + if (pkp) + pkp->SetNext(kpp); + else + xdp->SetToKeyParts(kpp); + + pkp= kpp; + } // endfor k + + xdp->SetNParts(kp.key_parts); + + if (pxd) + pxd->SetNext(xdp); + else + toidx= xdp; + + pxd= xdp; + } // endfor n + + return toidx; +} // end of GetIndexInfo + +const char *ha_connect::GetDBName(const char* name) +{ + return (name) ? name : table->s->db.str; +} // end of GetDBName + +const char *ha_connect::GetTableName(void) +{ + return (tshp) ? tshp->table_name.str : table->s->table_name.str; +} // end of GetTableName + +/****************************************************************************/ +/* Returns the column real or special name length of a field. */ +/****************************************************************************/ +int ha_connect::GetColNameLen(Field *fp) +{ + int n; + PFOS fop= GetFieldOptionStruct(fp); + + // Now get the column name length + if (fop && fop->special) + n= strlen(fop->special) + 1; + else + n= strlen(fp->field_name) + 1; + + return n; +} // end of GetColNameLen + +/****************************************************************************/ +/* Returns the column real or special name of a field. */ +/****************************************************************************/ +char *ha_connect::GetColName(Field *fp) +{ + PFOS fop= GetFieldOptionStruct(fp); + + return (fop && fop->special) ? fop->special : (char*)fp->field_name; +} // end of GetColName + +/****************************************************************************/ +/* Adds the column real or special name of a field to a string. */ +/****************************************************************************/ +void ha_connect::AddColName(char *cp, Field *fp) +{ + PFOS fop= GetFieldOptionStruct(fp); + + // Now add the column name + if (fop && fop->special) + // The prefix * mark the column as "special" + strcat(strcpy(cp, "*"), strupr(fop->special)); + else + strcpy(cp, (char*)fp->field_name); + +} // end of AddColName + +/****************************************************************************/ +/* Get the table description block of a CONNECT table. */ +/****************************************************************************/ +PTDB ha_connect::GetTDB(PGLOBAL g) +{ + const char *table_name; + PTDB tp; + + // Double test to be on the safe side + if (!g || !table) + return NULL; + + table_name= GetTableName(); + + if (tdbp && !stricmp(tdbp->GetName(), table_name) + && tdbp->GetMode() == xmod && !xp->CheckQuery(valid_query_id)) { + tp= tdbp; + tp->SetMode(xmod); + } else if ((tp= CntGetTDB(g, table_name, xmod, this))) + valid_query_id= xp->last_query_id; + else + printf("GetTDB: %s\n", g->Message); + + return tp; +} // end of GetTDB + +/****************************************************************************/ +/* Open a CONNECT table, restricting column list if cols is true. */ +/****************************************************************************/ +bool ha_connect::OpenTable(PGLOBAL g, bool del) +{ + bool rc= false; + char *c1= NULL, *c2=NULL; + + // Double test to be on the safe side + if (!g || !table) { + printf("OpenTable logical error; g=%p table=%p\n", g, table); + return true; + } // endif g + + if (!(tdbp= GetTDB(g))) + return true; + else if (tdbp->IsReadOnly()) + switch (xmod) { + case MODE_WRITE: + case MODE_INSERT: + case MODE_UPDATE: + case MODE_DELETE: + strcpy(g->Message, MSG(READ_ONLY)); + return true; + default: + break; + } // endswitch xmode + + if (xmod != MODE_INSERT) { + // Get the list of used fields (columns) + char *p; + unsigned int k1, k2, n1, n2; + Field* *field; + MY_BITMAP *map= table->read_set; + MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL; + + k1= k2= 0; + n1= n2= 1; // 1 is space for final null character + + for (field= table->field; *field; field++) { + if (bitmap_is_set(map, (*field)->field_index)) { + n1+= (GetColNameLen(*field) + 1); + k1++; + } // endif + + if (ump && bitmap_is_set(ump, (*field)->field_index)) { + n2+= GetColNameLen(*field); + k2++; + } // endif + + } // endfor field + + if (k1) { + p= c1= (char*)PlugSubAlloc(g, NULL, n1); + + for (field= table->field; *field; field++) + if (bitmap_is_set(map, (*field)->field_index)) { + AddColName(p, *field); + p+= (strlen(p) + 1); + } // endif used field + + *p= '\0'; // mark end of list + } // endif k1 + + if (k2) { + p= c2= (char*)PlugSubAlloc(g, NULL, n2); + + for (field= table->field; *field; field++) + if (bitmap_is_set(ump, (*field)->field_index)) { + AddColName(p, *field); + p+= (strlen(p) + 1); + } // endif used field + + *p= '\0'; // mark end of list + } // endif k2 + + } // endif xmod + + // Open the table + if (!(rc= CntOpenTable(g, tdbp, xmod, c1, c2, del, this))) { + istable= true; +// strmake(tname, table_name, sizeof(tname)-1); + + // We may be in a create index query + if (xmod == MODE_ANY && *tdbp->GetName() != '#') { + // The current indexes + PIXDEF oldpix= GetIndexInfo(); + } // endif xmod + +// tdbp->SetOrig((PTBX)table); // used by CheckCond + } else + printf("OpenTable: %s\n", g->Message); + + if (rc) { + tdbp= NULL; + valid_info= false; + } // endif rc + + return rc; +} // end of OpenTable + + +/****************************************************************************/ +/* IsOpened: returns true if the table is already opened. */ +/****************************************************************************/ +bool ha_connect::IsOpened(void) +{ + return (!xp->CheckQuery(valid_query_id) && tdbp + && tdbp->GetUse() == USE_OPEN); +} // end of IsOpened + + +/****************************************************************************/ +/* Close a CONNECT table. */ +/****************************************************************************/ +int ha_connect::CloseTable(PGLOBAL g) +{ + int rc= CntCloseTable(g, tdbp); + tdbp= NULL; + sdvalin=NULL; + sdvalout=NULL; + valid_info= false; + indexing= -1; + return rc; +} // end of CloseTable + + +/***********************************************************************/ +/* Make a pseudo record from current row values. Specific to MySQL. */ +/***********************************************************************/ +int ha_connect::MakeRecord(char *buf) +{ + char *p, *fmt, val[32]; + int rc= 0; + Field* *field; + Field *fp; + my_bitmap_map *org_bitmap; + CHARSET_INFO *charset= tdbp->data_charset(); + const MY_BITMAP *map; + PVAL value; + PCOL colp= NULL; + DBUG_ENTER("ha_connect::MakeRecord"); + + if (xtrace > 1) +#if defined(MARIADB) + printf("Maps: read=%08X write=%08X vcol=%08X defr=%08X defw=%08X\n", + *table->read_set->bitmap, *table->write_set->bitmap, + *table->vcol_set->bitmap, + *table->def_read_set.bitmap, *table->def_write_set.bitmap); +#else // !MARIADB + printf("Maps: read=%p write=%p defr=%p defw=%p\n", + *table->read_set->bitmap, *table->write_set->bitmap, + *table->def_read_set.bitmap, *table->def_write_set.bitmap); +#endif // !MARIADB + + // Avoid asserts in field::store() for columns that are not updated + org_bitmap= dbug_tmp_use_all_columns(table, table->write_set); + + // This is for variable_length rows + memset(buf, 0, table->s->null_bytes); + + // When sorting read_set selects all columns, so we use def_read_set + map= (const MY_BITMAP *)&table->def_read_set; + + // Make the pseudo record from field values + for (field= table->field; *field && !rc; field++) { + fp= *field; + +#if defined(MARIADB) + if (fp->vcol_info && !fp->stored_in_db) + continue; // This is a virtual column +#endif // MARIADB + + if (bitmap_is_set(map, fp->field_index)) { + // This is a used field, fill the buffer with value + for (colp= tdbp->GetColumns(); colp; colp= colp->GetNext()) + if (!stricmp(colp->GetName(), GetColName(fp))) + break; + + if (!colp) { + printf("Column %s not found\n", fp->field_name); + dbug_tmp_restore_column_map(table->write_set, org_bitmap); + DBUG_RETURN(HA_ERR_WRONG_IN_RECORD); + } // endif colp + + value= colp->GetValue(); + + // All this could be better optimized + if (!value->IsNull()) { + switch (value->GetType()) { + case TYPE_DATE: + if (!sdvalout) + sdvalout= AllocateValue(xp->g, TYPE_STRING, 20); + + switch (fp->type()) { + case MYSQL_TYPE_DATE: + fmt= "%Y-%m-%d"; + break; + case MYSQL_TYPE_TIME: + fmt= "%H:%M:%S"; + break; + default: + fmt= "%Y-%m-%d %H:%M:%S"; + } // endswitch type + + // Get date in the format required by MySQL fields + value->FormatValue(sdvalout, fmt); + p= sdvalout->GetCharValue(); + break; + case TYPE_FLOAT: + p= NULL; + break; + case TYPE_STRING: + // Passthru + default: + p= value->GetCharString(val); + } // endswitch Type + + if (p) { + if (fp->store(p, strlen(p), charset, CHECK_FIELD_WARN)) { + // Avoid "error" on null fields + if (value->GetIntValue()) + rc= HA_ERR_WRONG_IN_RECORD; + + DBUG_PRINT("MakeRecord", ("%s", p)); + } // endif store + + } else + if (fp->store(value->GetFloatValue())) { + rc= HA_ERR_WRONG_IN_RECORD; + DBUG_PRINT("MakeRecord", ("%s", value->GetCharString(val))); + } // endif store + + fp->set_notnull(); + } else + fp->set_null(); + + } // endif bitmap + + } // endfor field + + // This is copied from ha_tina and is necessary to avoid asserts + dbug_tmp_restore_column_map(table->write_set, org_bitmap); + DBUG_RETURN(rc); +} // end of MakeRecord + + +/***********************************************************************/ +/* Set row values from a MySQL pseudo record. Specific to MySQL. */ +/***********************************************************************/ +int ha_connect::ScanRecord(PGLOBAL g, uchar *buf) +{ + char attr_buffer[1024]; + char data_buffer[1024]; + char *fmt; + int rc= 0; + PCOL colp; + PVAL value; + Field *fp; + PTDBASE tp= (PTDBASE)tdbp; + String attribute(attr_buffer, sizeof(attr_buffer), + table->s->table_charset); + my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set); + const CHARSET_INFO *charset= tdbp->data_charset(); + String data_charset_value(data_buffer, sizeof(data_buffer), charset); + + // Scan the pseudo record for field values and set column values + for (Field **field=table->field ; *field ; field++) { + fp= *field; + +#if defined(MARIADB) + if ((fp->vcol_info && !fp->stored_in_db) || + fp->option_struct->special) + continue; // Is a virtual column possible here ??? +#endif // MARIADB + + if (xmod == MODE_INSERT || + bitmap_is_set(table->write_set, fp->field_index)) { + for (colp= tp->GetSetCols(); colp; colp= colp->GetNext()) + if (!stricmp(colp->GetName(), fp->field_name)) + break; + + if (!colp) { + printf("Column %s not found\n", fp->field_name); + rc= HA_ERR_WRONG_IN_RECORD; + goto err; + } else + value= colp->GetValue(); + + // This is a used field, fill the value from the row buffer + // All this could be better optimized + if (fp->is_null()) { + if (colp->IsNullable()) + value->SetNull(true); + + value->Reset(); + } else switch (value->GetType()) { + case TYPE_FLOAT: + value->SetValue(fp->val_real()); + break; + case TYPE_DATE: + if (!sdvalin) { + sdvalin= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19); + + // Get date in the format produced by MySQL fields + switch (fp->type()) { + case MYSQL_TYPE_DATE: + fmt= "YYYY-MM-DD"; + break; + case MYSQL_TYPE_TIME: + fmt= "hh:mm:ss"; + break; + default: + fmt= "YYYY-MM-DD hh:mm:ss"; + } // endswitch type + + ((DTVAL*)sdvalin)->SetFormat(g, fmt, strlen(fmt)); + } // endif sdvalin + + fp->val_str(&attribute); + sdvalin->SetValue_psz(attribute.c_ptr_safe()); + value->SetValue_pval(sdvalin); + break; + default: + fp->val_str(&attribute); + if (charset == &my_charset_bin) + { + value->SetValue_psz(attribute.c_ptr_safe()); + } + else + { + // Convert from SQL field charset to DATA_CHARSET + uint cnv_errors; + data_charset_value.copy(attribute.ptr(), attribute.length(), + attribute.charset(), charset, &cnv_errors); + value->SetValue_psz(data_charset_value.c_ptr_safe()); + } + } // endswitch Type + +#ifdef NEWCHANGE + } else if (xmod == MODE_UPDATE) { + PCOL cp; + + for (cp= tp->GetColumns(); cp; cp= cp->GetNext()) + if (!stricmp(colp->GetName(), cp->GetName())) + break; + + if (!cp) { + rc= HA_ERR_WRONG_IN_RECORD; + goto err; + } // endif cp + + value->SetValue_pval(cp->GetValue()); + } else // mode Insert + value->Reset(); +#else + } // endif bitmap_is_set +#endif + + } // endfor field + + err: + dbug_tmp_restore_column_map(table->read_set, bmap); + return rc; +} // end of ScanRecord + + +/***********************************************************************/ +/* Check change in index column. Specific to MySQL. */ +/* Should be elaborated to check for real changes. */ +/***********************************************************************/ +int ha_connect::CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf) +{ + return ScanRecord(g, newbuf); +} // end of dummy CheckRecord + + +/***********************************************************************/ +/* Return the string representing an operator. */ +/***********************************************************************/ +const char *ha_connect::GetValStr(OPVAL vop, bool neg) +{ + const char *val; + + switch (vop) { + case OP_EQ: + val= " = "; + break; + case OP_NE: + val= " <> "; + break; + case OP_GT: + val= " > "; + break; + case OP_GE: + val= " >= "; + break; + case OP_LT: + val= " < "; + break; + case OP_LE: + val= " <= "; + break; + case OP_IN: + val= (neg) ? " NOT IN (" : " IN ("; + break; + case OP_NULL: + val= " IS NULL"; + break; + case OP_LIKE: + val= " LIKE "; + break; + case OP_XX: + val= " BETWEEN "; + break; + case OP_EXIST: + val= " EXISTS "; + break; + case OP_AND: + val= " AND "; + break; + case OP_OR: + val= " OR "; + break; + case OP_NOT: + val= " NOT "; + break; + case OP_CNC: + val= " || "; + break; + case OP_ADD: + val= " + "; + break; + case OP_SUB: + val= " - "; + break; + case OP_MULT: + val= " * "; + break; + case OP_DIV: + val= " / "; + break; + default: + val= " ? "; + } /* endswitch */ + + return val; +} // end of GetValStr + + +/***********************************************************************/ +/* Check the WHERE condition and return an ODBC/WQL filter. */ +/***********************************************************************/ +PFIL ha_connect::CheckCond(PGLOBAL g, PFIL filp, AMT tty, Item *cond) +{ + unsigned int i; + bool ismul= false; + PPARM pfirst= NULL, pprec= NULL, pp[2]= {NULL, NULL}; + OPVAL vop= OP_XX; + + if (!cond) + return NULL; + + if (xtrace > 1) + printf("Cond type=%d\n", cond->type()); + + if (cond->type() == COND::COND_ITEM) { + char *p1, *p2; + Item_cond *cond_item= (Item_cond *)cond; + + if (xtrace > 1) + printf("Cond: Ftype=%d name=%s\n", cond_item->functype(), + cond_item->func_name()); + + switch (cond_item->functype()) { + case Item_func::COND_AND_FUNC: vop= OP_AND; break; + case Item_func::COND_OR_FUNC: vop= OP_OR; break; + default: return NULL; + } // endswitch functype + + List<Item>* arglist= cond_item->argument_list(); + List_iterator<Item> li(*arglist); + Item *subitem; + + p1= filp + strlen(filp); + strcpy(p1, "("); + p2= p1 + 1; + + for (i= 0; i < arglist->elements; i++) + if ((subitem= li++)) { + if (!CheckCond(g, filp, tty, subitem)) { + if (vop == OP_OR) + return NULL; + else + *p2= 0; + + } else { + p1= p2 + strlen(p2); + strcpy(p1, GetValStr(vop, FALSE)); + p2= p1 + strlen(p1); + } // endif CheckCond + + } else + return NULL; + + if (*p1 != '(') + strcpy(p1, ")"); + else + return NULL; + + } else if (cond->type() == COND::FUNC_ITEM) { + unsigned int i; +// int n; + bool iscol, neg= FALSE; + Item_func *condf= (Item_func *)cond; + Item* *args= condf->arguments(); + + if (xtrace > 1) + printf("Func type=%d argnum=%d\n", condf->functype(), + condf->argument_count()); + +// neg= condf-> + + switch (condf->functype()) { + case Item_func::EQUAL_FUNC: + case Item_func::EQ_FUNC: vop= OP_EQ; break; + case Item_func::NE_FUNC: vop= OP_NE; break; + case Item_func::LT_FUNC: vop= OP_LT; break; + case Item_func::LE_FUNC: vop= OP_LE; break; + case Item_func::GE_FUNC: vop= OP_GE; break; + case Item_func::GT_FUNC: vop= OP_GT; break; + case Item_func::IN_FUNC: vop= OP_IN; + neg= ((Item_func_opt_neg *)condf)->negated; + case Item_func::BETWEEN: ismul= true; break; + default: return NULL; + } // endswitch functype + + if (condf->argument_count() < 2) + return NULL; + else if (ismul && tty == TYPE_AM_WMI) + return NULL; // Not supported by WQL + + for (i= 0; i < condf->argument_count(); i++) { + if (xtrace > 1) + printf("Argtype(%d)=%d\n", i, args[i]->type()); + + if (i >= 2 && !ismul) { + if (xtrace > 1) + printf("Unexpected arg for vop=%d\n", vop); + + continue; + } // endif i + + if ((iscol= args[i]->type() == COND::FIELD_ITEM)) { + const char *fnm; + ha_field_option_struct *fop; + Item_field *pField= (Item_field *)args[i]; + + if (pField->field->table != table) + return NULL; // Field does not belong to this table + else + fop= GetFieldOptionStruct(pField->field); + + if (fop && fop->special) { + if (tty == TYPE_AM_TBL && !stricmp(fop->special, "TABID")) + fnm= "TABID"; + else + return NULL; + + } else if (tty == TYPE_AM_TBL) + return NULL; + else + fnm= pField->field->field_name; + + if (xtrace > 1) { + printf("Field index=%d\n", pField->field->field_index); + printf("Field name=%s\n", pField->field->field_name); + } // endif xtrace + + // IN and BETWEEN clauses should be col VOP list + if (i && ismul) + return NULL; + + strcat(filp, fnm); + } else { + char buff[256]; + String *res, tmp(buff,sizeof(buff), &my_charset_bin); + Item_basic_constant *pval= (Item_basic_constant *)args[i]; + + if ((res= pval->val_str(&tmp)) == NULL) + return NULL; // To be clarified + + if (xtrace > 1) + printf("Value=%.*s\n", res->length(), res->ptr()); + + // IN and BETWEEN clauses should be col VOP list + if (!i && ismul) + return NULL; + + // Append the value to the filter + if (args[i]->type() == COND::STRING_ITEM) + strcat(strcat(strcat(filp, "'"), res->ptr()), "'"); + else + strncat(filp, res->ptr(), res->length()); + + } // endif + + if (!i) + strcat(filp, GetValStr(vop, neg)); + else if (vop == OP_XX && i == 1) + strcat(filp, " AND "); + else if (vop == OP_IN) + strcat(filp, (i == condf->argument_count() - 1) ? ")" : ","); + + } // endfor i + + } else { + if (xtrace > 1) + printf("Unsupported condition\n"); + + return NULL; + } // endif's type + + return filp; +} // end of CheckCond + + + /** + Push condition down to the table handler. + + @param cond Condition to be pushed. The condition tree must not be + modified by the caller. + + @return + The 'remainder' condition that caller must use to filter out records. + NULL means the handler will not return rows that do not match the + passed condition. + + @note + CONNECT handles the filtering only for table types that construct + an SQL or WQL query, but still leaves it to MySQL because only some + parts of the filter may be relevant. + The first suballocate finds the position where the string will be + constructed in the sarea. The second one does make the suballocation + with the proper length. + */ +const COND *ha_connect::cond_push(const COND *cond) +{ + DBUG_ENTER("ha_connect::cond_push"); + + if (tdbp) { + AMT tty= tdbp->GetAmType(); + + if (tty == TYPE_AM_WMI || tty == TYPE_AM_ODBC || + tty == TYPE_AM_TBL || tty == TYPE_AM_MYSQL) { + PGLOBAL& g= xp->g; + PFIL filp= (PFIL)PlugSubAlloc(g, NULL, 0); + + *filp= 0; + + if (CheckCond(g, filp, tty, (Item *)cond)) { + if (xtrace) + puts(filp); + + tdbp->SetFilter(filp); +// cond= NULL; // This does not work anyway + PlugSubAlloc(g, NULL, strlen(filp) + 1); + } // endif filp + + } // endif tty + + } // endif tdbp + + // Let MySQL do the filtering + DBUG_RETURN(cond); +} // end of cond_push + +/** + Number of rows in table. It will only be called if + (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0 +*/ +ha_rows ha_connect::records() +{ + if (!valid_info) + info(HA_STATUS_VARIABLE); + + if (tdbp && tdbp->Cardinality(NULL)) + return stats.records; + else + return HA_POS_ERROR; + +} // end of records + + +/** + Return an error message specific to this handler. + + @param error error code previously returned by handler + @param buf pointer to String where to add error message + + @return + Returns true if this is a temporary error +*/ +bool ha_connect::get_error_message(int error, String* buf) +{ + DBUG_ENTER("ha_connect::get_error_message"); + + if (xp && xp->g) + buf->copy(xp->g->Message, (uint)strlen(xp->g->Message), + system_charset_info); + + DBUG_RETURN(false); +} // end of get_error_message + + +/** + @brief + If frm_error() is called then we will use this to determine + the file extensions that exist for the storage engine. This is also + used by the default rename_table and delete_table method in + handler.cc. + + For engines that have two file name extentions (separate meta/index file + and data file), the order of elements is relevant. First element of engine + file name extentions array should be meta/index file extention. Second + element - data file extention. This order is assumed by + prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued. + + @note: PlugDB will handle all file creation/deletion. When dropping + a CONNECT table, we don't want the PlugDB table to be dropped or erased. + Therefore we provide a void list of extensions. + + @see + rename_table method in handler.cc and + delete_table method in handler.cc +*/ +static const char *ha_connect_null_exts[]= { + NullS +}; + +static const char* *ha_connect_exts= ha_connect_null_exts; + +const char **ha_connect::bas_ext() const +{ + return ha_connect_exts; // a null list, see @note above +} // end of bas_ext + + +/** + @brief + Used for opening tables. The name will be the name of the file. + + @details + A table is opened when it needs to be opened; e.g. when a request comes in + for a SELECT on the table (tables are not open and closed for each request, + they are cached). + + Called from handler.cc by handler::ha_open(). The server opens all tables by + calling ha_open() which then calls the handler specific open(). + + @note + For CONNECT no open can be done here because field information is not yet + updated. >>>>> TO BE CHECKED <<<<< + (Thread information could be get by using 'ha_thd') + + @see + handler::ha_open() in handler.cc +*/ +int ha_connect::open(const char *name, int mode, uint test_if_locked) +{ + int rc= 0; + DBUG_ENTER("ha_connect::open"); + + if (xtrace) + printf("open: name=%s mode=%d test=%ud\n", name, mode, test_if_locked); + + if (!(share= get_share(name, table))) + DBUG_RETURN(1); + + thr_lock_data_init(&share->lock,&lock,NULL); + + // Try to get the user if possible + if (table && table->in_use) { + PGLOBAL g= GetPlug(table->in_use); + + // Try to set the database environment + if (g) + rc= (CntCheckDB(g, this, name)) ? (-2) : 0; + + } // endif table + + DBUG_RETURN(rc); +} // end of open + +/** + @brief + Make the indexes for this table +*/ +int ha_connect::optimize(THD* thd, HA_CHECK_OPT* check_opt) +{ + int rc= 0; + PGLOBAL& g= xp->g; + PDBUSER dup= PlgGetUser(g); + + // Ignore error on the opt file + dup->Check &= ~CHK_OPT; + tdbp= GetTDB(g); + dup->Check |= CHK_OPT; + + if (tdbp || (tdbp= GetTDB(g))) { + if (!((PTDBASE)tdbp)->GetDef()->Indexable()) { + sprintf(g->Message, "Table %s is not indexable", tdbp->GetName()); + rc= HA_ERR_INTERNAL_ERROR; + } else if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, true))) { + if (rc == RC_INFO) { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + rc= 0; + } else + rc = HA_ERR_INTERNAL_ERROR; + + } // endif's + + } else + rc= HA_ERR_INTERNAL_ERROR; + + return rc; +} // end of optimize + +/** + @brief + Closes a table. We call the free_share() function to free any resources + that we have allocated in the "shared" structure. + + @details + Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is + only used to close up temporary tables or during the process where a + temporary table is converted over to being a myisam table. + + For sql_base.cc look at close_data_tables(). + + @see + sql_base.cc, sql_select.cc and table.cc +*/ +int ha_connect::close(void) +{ + int rc= 0; + DBUG_ENTER("ha_connect::close"); + + // If this is called by a later query, the table may have + // been already closed and the tdbp is not valid anymore. + if (tdbp && xp->last_query_id == valid_query_id) + rc= CloseTable(xp->g); + + DBUG_RETURN(free_share(share) || rc); +} // end of close + + +/** + @brief + write_row() inserts a row. No extra() hint is given currently if a bulk load + is happening. buf() is a byte array of data. You can use the field + information to extract the data from the native byte array type. + + @details + Example of this would be: + @code + for (Field **field=table->field ; *field ; field++) + { + ... + } + @endcode + + See ha_tina.cc for an example of extracting all of the data as strings. + ha_berekly.cc has an example of how to store it intact by "packing" it + for ha_berkeley's own native storage type. + + See the note for update_row() on auto_increments and timestamps. This + case also applies to write_row(). + + Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc, + sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc. + + @see + item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc, + sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc +*/ +int ha_connect::write_row(uchar *buf) +{ + int rc= 0; + PGLOBAL& g= xp->g; + DBUG_ENTER("ha_connect::write_row"); + + // Open the table if it was not opened yet (possible ???) + if (!IsOpened()) + if (OpenTable(g)) { + if (strstr(g->Message, "read only")) + rc= HA_ERR_TABLE_READONLY; + else + rc= HA_ERR_INITIALIZATION; + + DBUG_RETURN(rc); + } // endif tdbp + + if (tdbp->GetMode() == MODE_ANY) + DBUG_RETURN(0); + +#if 0 // AUTO_INCREMENT NIY + if (table->next_number_field && buf == table->record[0]) { + int error; + + if ((error= update_auto_increment())) + return error; + + } // endif nex_number_field +#endif // 0 + + // Set column values from the passed pseudo record + if ((rc= ScanRecord(g, buf))) + DBUG_RETURN(rc); + + // Return result code from write operation + if (CntWriteRow(g, tdbp)) { + DBUG_PRINT("write_row", ("%s", g->Message)); + printf("write_row: %s\n", g->Message); + rc= HA_ERR_INTERNAL_ERROR; + } // endif RC + + DBUG_RETURN(rc); +} // end of write_row + + +/** + @brief + Yes, update_row() does what you expect, it updates a row. old_data will have + the previous row record in it, while new_data will have the newest data in it. + Keep in mind that the server can do updates based on ordering if an ORDER BY + clause was used. Consecutive ordering is not guaranteed. + + @details + Currently new_data will not have an updated auto_increament record, or + and updated timestamp field. You can do these for example by doing: + @code + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); + if (table->next_number_field && record == table->record[0]) + update_auto_increment(); + @endcode + + Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc. + + @see + sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc +*/ +int ha_connect::update_row(const uchar *old_data, uchar *new_data) +{ + int rc= 0; + PGLOBAL& g= xp->g; + DBUG_ENTER("ha_connect::update_row"); + + if (xtrace > 1) + printf("update_row: old=%s new=%s\n", old_data, new_data); + + // Check values for possible change in indexed column + if ((rc= CheckRecord(g, old_data, new_data))) + return rc; + + if (CntUpdateRow(g, tdbp)) { + DBUG_PRINT("update_row", ("%s", g->Message)); + printf("update_row CONNECT: %s\n", g->Message); + rc= HA_ERR_INTERNAL_ERROR; + } // endif RC + + DBUG_RETURN(rc); +} // end of update_row + + +/** + @brief + This will delete a row. buf will contain a copy of the row to be deleted. + The server will call this right after the current row has been called (from + either a previous rnd_nexT() or index call). + + @details + If you keep a pointer to the last row or can access a primary key it will + make doing the deletion quite a bit easier. Keep in mind that the server does + not guarantee consecutive deletions. ORDER BY clauses can be used. + + Called in sql_acl.cc and sql_udf.cc to manage internal table + information. Called in sql_delete.cc, sql_insert.cc, and + sql_select.cc. In sql_select it is used for removing duplicates + while in insert it is used for REPLACE calls. + + @see + sql_acl.cc, sql_udf.cc, sql_delete.cc, sql_insert.cc and sql_select.cc +*/ +int ha_connect::delete_row(const uchar *buf) +{ + int rc= 0; + DBUG_ENTER("ha_connect::delete_row"); + + if (CntDeleteRow(xp->g, tdbp, false)) { + rc= HA_ERR_INTERNAL_ERROR; + printf("delete_row CONNECT: %s\n", xp->g->Message); + } // endif DeleteRow + + DBUG_RETURN(rc); +} // end of delete_row + + +/****************************************************************************/ +/* We seem to come here at the begining of an index use. */ +/****************************************************************************/ +int ha_connect::index_init(uint idx, bool sorted) +{ + int rc; + PGLOBAL& g= xp->g; + DBUG_ENTER("index_init"); + + if ((rc= rnd_init(0))) + return rc; + + indexing= CntIndexInit(g, tdbp, (signed)idx); + + if (indexing <= 0) { + DBUG_PRINT("index_init", ("%s", g->Message)); + printf("index_init CONNECT: %s\n", g->Message); + active_index= MAX_KEY; + rc= HA_ERR_INTERNAL_ERROR; + } else { + if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) { + if (((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) + ((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g); + + active_index= idx; + } else // Void table + indexing= 0; + + rc= 0; + } // endif indexing + + DBUG_RETURN(rc); +} // end of index_init + +/****************************************************************************/ +/* We seem to come here at the end of an index use. */ +/****************************************************************************/ +int ha_connect::index_end() +{ + DBUG_ENTER("index_end"); + active_index= MAX_KEY; + DBUG_RETURN(rnd_end()); +} // end of index_end + + +/****************************************************************************/ +/* This is internally called by all indexed reading functions. */ +/****************************************************************************/ +int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len) +{ + int rc; + +//statistic_increment(ha_read_key_count, &LOCK_status); + + switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len)) { + case RC_OK: + xp->fnd++; + rc= MakeRecord((char*)buf); + break; + case RC_EF: // End of file + rc= HA_ERR_END_OF_FILE; + break; + case RC_NF: // Not found + xp->nfd++; + rc= (op == OP_SAME) ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND; + break; + default: // Read error + DBUG_PRINT("ReadIndexed", ("%s", xp->g->Message)); + printf("ReadIndexed: %s\n", xp->g->Message); + rc= HA_ERR_INTERNAL_ERROR; + } // endswitch RC + + if (xtrace > 1) + printf("ReadIndexed: op=%d rc=%d\n", op, rc); + + table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND; + return rc; +} // end of ReadIndexed + + +#ifdef NOT_USED +/** + @brief + Positions an index cursor to the index specified in the handle. Fetches the + row if available. If the key value is null, begin at the first key of the + index. +*/ +int ha_connect::index_read_map(uchar *buf, const uchar *key, + key_part_map keypart_map __attribute__((unused)), + enum ha_rkey_function find_flag + __attribute__((unused))) +{ + DBUG_ENTER("ha_connect::index_read"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} +#endif // NOT_USED + + +/****************************************************************************/ +/* This is called by handler::index_read_map. */ +/****************************************************************************/ +int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len, + enum ha_rkey_function find_flag) +{ + int rc; + OPVAL op= OP_XX; + DBUG_ENTER("ha_connect::index_read"); + + switch(find_flag) { + case HA_READ_KEY_EXACT: op= OP_EQ; break; + case HA_READ_AFTER_KEY: op= OP_GT; break; + case HA_READ_KEY_OR_NEXT: op= OP_GE; break; + default: DBUG_RETURN(-1); + } // endswitch find_flag + + if (xtrace > 1) + printf("%p index_read: op=%d\n", this, op); + + if (indexing > 0) + rc= ReadIndexed(buf, op, key, key_len); + else + rc= HA_ERR_INTERNAL_ERROR; + + DBUG_RETURN(rc); +} // end of index_read + + +/** + @brief + Used to read forward through the index. +*/ +int ha_connect::index_next(uchar *buf) +{ + int rc; + DBUG_ENTER("ha_connect::index_next"); + //statistic_increment(ha_read_next_count, &LOCK_status); + + if (indexing > 0) + rc= ReadIndexed(buf, OP_NEXT); + else if (!indexing) + rc= rnd_next(buf); + else + rc= HA_ERR_INTERNAL_ERROR; + + DBUG_RETURN(rc); +} // end of index_next + + +#ifdef NOT_USED +/** + @brief + Used to read backwards through the index. +*/ +int ha_connect::index_prev(uchar *buf) +{ + DBUG_ENTER("ha_connect::index_prev"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} +#endif // NOT_USED + + +/** + @brief + index_first() asks for the first key in the index. + + @details + Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc. + + @see + opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc +*/ +int ha_connect::index_first(uchar *buf) +{ + int rc; + DBUG_ENTER("ha_connect::index_first"); + + if (indexing > 0) + rc= ReadIndexed(buf, OP_FIRST); + else if (indexing < 0) + rc= HA_ERR_INTERNAL_ERROR; + else if (CntRewindTable(xp->g, tdbp)) { + table->status= STATUS_NOT_FOUND; + rc= HA_ERR_INTERNAL_ERROR; + } else + rc= rnd_next(buf); + + DBUG_RETURN(rc); +} // end of index_first + + +#ifdef NOT_USED +/** + @brief + index_last() asks for the last key in the index. + + @details + Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc. + + @see + opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc +*/ +int ha_connect::index_last(uchar *buf) +{ + DBUG_ENTER("ha_connect::index_last"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} +#endif // NOT_USED + + +/****************************************************************************/ +/* This is called to get more rows having the same index value. */ +/****************************************************************************/ +int ha_connect::index_next_same(uchar *buf, const uchar *key, uint keylen) +{ + int rc; + DBUG_ENTER("ha_connect::index_next_same"); +//statistic_increment(ha_read_next_count, &LOCK_status); + + if (!indexing) + rc= rnd_next(buf); + else if (indexing > 0) + rc= ReadIndexed(buf, OP_SAME); + else + rc= HA_ERR_INTERNAL_ERROR; + + DBUG_RETURN(rc); +} // end of index_next_same + + +/** + @brief + rnd_init() is called when the system wants the storage engine to do a table + scan. See the example in the introduction at the top of this file to see when + rnd_init() is called. + + @details + Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, + and sql_update.cc. + + @note + We always call open and extern_lock/start_stmt before comming here. + + @see + filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc +*/ +int ha_connect::rnd_init(bool scan) +{ + PGLOBAL g= ((table && table->in_use) ? GetPlug(table->in_use) : + (xp) ? xp->g : NULL); + DBUG_ENTER("ha_connect::rnd_init"); + + if (xtrace) + printf("%p in rnd_init: scan=%d\n", this, scan); + + if (g) { + // Open the table if it was not opened yet (possible ???) + if (!IsOpened()) { + if (!table || xmod == MODE_INSERT) + DBUG_RETURN(HA_ERR_INITIALIZATION); + + if (OpenTable(g, xmod == MODE_DELETE)) +#if defined(MARIADB) + DBUG_RETURN(HA_ERR_INITIALIZATION); +#else // !MARIADB + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); +#endif // !MARIADB + + } else + void(CntRewindTable(g, tdbp)); // Read from beginning + + } // endif g + + xp->nrd= xp->fnd= xp->nfd= 0; + xp->tb1= my_interval_timer(); + DBUG_RETURN(0); +} // end of rnd_init + +/** + @brief + Not described. + + @note + The previous version said: + Stop scanning of table. Note that this may be called several times during + execution of a sub select. + =====> This has been moved to external lock to avoid closing subselect tables. +*/ +int ha_connect::rnd_end() +{ + int rc= 0; + DBUG_ENTER("ha_connect::rnd_end"); + + // If this is called by a later query, the table may have + // been already closed and the tdbp is not valid anymore. +// if (tdbp && xp->last_query_id == valid_query_id) +// rc= CloseTable(xp->g); + + DBUG_RETURN(rc); +} // end of rnd_end + + +/** + @brief + This is called for each row of the table scan. When you run out of records + you should return HA_ERR_END_OF_FILE. Fill buff up with the row information. + The Field structure for the table is the key to getting data into buf + in a manner that will allow the server to understand it. + + @details + Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, + and sql_update.cc. + + @see + filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc +*/ +int ha_connect::rnd_next(uchar *buf) +{ + int rc; + DBUG_ENTER("ha_connect::rnd_next"); +//statistic_increment(ha_read_rnd_next_count, &LOCK_status); + +#if !defined(MARIADB) + if (!tdbp) // MySQL ignores error from rnd_init + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); +#endif // !MARIADB + + if (tdbp->GetMode() == MODE_ANY) { + // We will stop on next read + if (!stop) { + stop= true; + DBUG_RETURN(RC_OK); + } else + DBUG_RETURN(HA_ERR_END_OF_FILE); + + } // endif Mode + + switch (CntReadNext(xp->g, tdbp)) { + case RC_OK: + rc= MakeRecord((char*)buf); + break; + case RC_EF: // End of file + rc= HA_ERR_END_OF_FILE; + break; + case RC_NF: // Not found + rc= HA_ERR_RECORD_DELETED; + break; + default: // Read error + printf("rnd_next CONNECT: %s\n", xp->g->Message); + rc= (records()) ? HA_ERR_INTERNAL_ERROR : HA_ERR_END_OF_FILE; + break; + } // endswitch RC + +#ifndef DBUG_OFF + if (rc || !(xp->nrd++ % 16384)) { + ulonglong tb2= my_interval_timer(); + double elapsed= (double) (tb2 - xp->tb1) / 1000000000ULL; + DBUG_PRINT("rnd_next", ("rc=%d nrd=%u fnd=%u nfd=%u sec=%.3lf\n", + rc, (uint)xp->nrd, (uint)xp->fnd, + (uint)xp->nfd, elapsed)); + xp->tb1= tb2; + xp->fnd= xp->nfd= 0; + } // endif nrd +#endif + + table->status= (!rc) ? 0 : STATUS_NOT_FOUND; + DBUG_RETURN(rc); +} // end of rnd_next + + +/** + @brief + position() is called after each call to rnd_next() if the data needs + to be ordered. You can do something like the following to store + the position: + @code + my_store_ptr(ref, ref_length, current_position); + @endcode + + @details + The server uses ref to store data. ref_length in the above case is + the size needed to store current_position. ref is just a byte array + that the server will maintain. If you are using offsets to mark rows, then + current_position should be the offset. If it is a primary key like in + BDB, then it needs to be a primary key. + + Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc. + + @see + filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc +*/ +void ha_connect::position(const uchar *record) +{ + DBUG_ENTER("ha_connect::position"); + if (((PTDBASE)tdbp)->GetDef()->Indexable()) + my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos()); + DBUG_VOID_RETURN; +} // end of position + + +/** + @brief + This is like rnd_next, but you are given a position to use + to determine the row. The position will be of the type that you stored in + ref. You can use my_get_ptr(pos,ref_length) to retrieve whatever key + or position you saved when position() was called. + + @details + Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc. + + @note + Is this really useful? It was never called even when sorting. + + @see + filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc +*/ +int ha_connect::rnd_pos(uchar *buf, uchar *pos) +{ + int rc; + PTDBASE tp= (PTDBASE)tdbp; + DBUG_ENTER("ha_connect::rnd_pos"); + + if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) + rc= rnd_next(buf); + else + rc= HA_ERR_KEY_NOT_FOUND; + + DBUG_RETURN(rc); +} // end of rnd_pos + + +/** + @brief + ::info() is used to return information to the optimizer. See my_base.h for + the complete description. + + @details + Currently this table handler doesn't implement most of the fields really needed. + SHOW also makes use of this data. + + You will probably want to have the following in your code: + @code + if (records < 2) + records= 2; + @endcode + The reason is that the server will optimize for cases of only a single + record. If, in a table scan, you don't know the number of records, it + will probably be better to set records to two so you can return as many + records as you need. Along with records, a few more variables you may wish + to set are: + records + deleted + data_file_length + index_file_length + delete_length + check_time + Take a look at the public variables in handler.h for more information. + + Called in filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, + sql_delete.cc, sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, + sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, + sql_table.cc, sql_union.cc, and sql_update.cc. + + @see + filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc, + sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc, + sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc, + sql_union.cc and sql_update.cc +*/ +int ha_connect::info(uint flag) +{ + bool pure= false; + PGLOBAL g= GetPlug((table) ? table->in_use : NULL); + + DBUG_ENTER("ha_connect::info"); + + if (xtrace) + printf("%p In info: flag=%u valid_info=%d\n", this, flag, valid_info); + + if (!valid_info) { + // tdbp must be available to get updated info + if (xp->CheckQuery(valid_query_id) || !tdbp) { + if (xmod == MODE_ANY) { // Pure info, not a query + pure= true; + xp->CheckCleanup(); + } // endif xmod + +// tdbp= OpenTable(g, xmod == MODE_DELETE); + tdbp= GetTDB(g); + } // endif tdbp + + valid_info= CntInfo(g, tdbp, &xinfo); + } // endif valid_info + + if (flag & HA_STATUS_VARIABLE) { + stats.records= xinfo.records; + stats.deleted= 0; + stats.data_file_length= xinfo.data_file_length; + stats.index_file_length= 0; + stats.delete_length= 0; + stats.check_time= 0; + stats.mean_rec_length= xinfo.mean_rec_length; + } // endif HA_STATUS_VARIABLE + + if (flag & HA_STATUS_CONST) { + // This is imported from the previous handler and must be reconsidered + stats.max_data_file_length= LL(4294967295); + stats.max_index_file_length= LL(4398046510080); + stats.create_time= 0; + data_file_name= xinfo.data_file_name; + index_file_name= NULL; +// sortkey= (uint) - 1; // Table is not sorted + ref_length= sizeof(int); // Pointer size to row + table->s->db_options_in_use= 03; + stats.block_size= 1024; + table->s->keys_in_use.set_prefix(table->s->keys); + table->s->keys_for_keyread= table->s->keys_in_use; +// table->s->keys_for_keyread.subtract(table->s->read_only_keys); + table->s->db_record_offset= 0; + } // endif HA_STATUS_CONST + + if (flag & HA_STATUS_ERRKEY) { + errkey= 0; + } // endif HA_STATUS_ERRKEY + + if (flag & HA_STATUS_TIME) + stats.update_time= 0; + + if (flag & HA_STATUS_AUTO) + stats.auto_increment_value= 1; + + if (tdbp && pure) + CloseTable(g); // Not used anymore + + DBUG_RETURN(0); +} // end of info + + +/** + @brief + extra() is called whenever the server wishes to send a hint to + the storage engine. The myisam engine implements the most hints. + ha_innodb.cc has the most exhaustive list of these hints. + + @note + This is not yet implemented for CONNECT. + + @see + ha_innodb.cc +*/ +int ha_connect::extra(enum ha_extra_function operation) +{ + DBUG_ENTER("ha_connect::extra"); + DBUG_RETURN(0); +} // end of extra + + +/** + @brief + Used to delete all rows in a table, including cases of truncate and cases where + the optimizer realizes that all rows will be removed as a result of an SQL statement. + + @details + Called from item_sum.cc by Item_func_group_concat::clear(), + Item_sum_count_distinct::clear(), and Item_func_group_concat::clear(). + Called from sql_delete.cc by mysql_delete(). + Called from sql_select.cc by JOIN::reinit(). + Called from sql_union.cc by st_select_lex_unit::exec(). + + @see + Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and + Item_func_group_concat::clear() in item_sum.cc; + mysql_delete() in sql_delete.cc; + JOIN::reinit() in sql_select.cc and + st_select_lex_unit::exec() in sql_union.cc. +*/ +int ha_connect::delete_all_rows() +{ + int rc= 0; + PGLOBAL g= xp->g; + DBUG_ENTER("ha_connect::delete_all_rows"); + + // Close and reopen the table so it will be deleted + rc= CloseTable(g); + + if (!(OpenTable(g))) { + if (CntDeleteRow(g, tdbp, true)) { + printf("%s\n", g->Message); + rc= HA_ERR_INTERNAL_ERROR; + } // endif + + } else + rc= HA_ERR_INITIALIZATION; + + DBUG_RETURN(rc); +} // end of delete_all_rows + + +bool ha_connect::check_privileges(THD *thd, PTOS options) +{ + if (!options || !options->type) + goto err; + + switch (GetTypeID(options->type)) + { + case TAB_UNDEF: + case TAB_CATLG: + case TAB_PLG: + case TAB_JCT: + case TAB_DMY: + case TAB_NIY: + case TAB_PIVOT: + goto err; + + case TAB_DOS: + case TAB_FIX: + case TAB_BIN: + case TAB_CSV: + case TAB_FMT: + case TAB_DBF: + case TAB_XML: + case TAB_INI: + case TAB_VEC: + if (!options->filename) + return false; + char path[FN_REFLEN]; + (void) fn_format(path, options->filename, mysql_real_data_home, "", + MY_RELATIVE_PATH | MY_UNPACK_FILENAME); + if (!is_secure_file_path(path)) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + return true; + } + /* Fall through to check FILE_ACL */ + + case TAB_ODBC: + case TAB_MYSQL: + case TAB_DIR: + case TAB_MAC: + case TAB_WMI: + case TAB_OEM: + return check_access(thd, FILE_ACL, NULL, NULL, NULL, 0, 0); + + case TAB_TBL: + return false; + } + +err: + my_printf_error(ER_UNKNOWN_ERROR, "check_privileges failed", MYF(0)); + return true; +} + +// Check that two indexes are equivalent +bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2) +{ + bool b= true; + PKPDEF kp1, kp2; + + if (stricmp(xp1->Name, xp2->Name)) + b= false; + else if (xp1->Nparts != xp2->Nparts || + xp1->MaxSame != xp2->MaxSame || + xp1->Unique != xp2->Unique) + b= false; + else for (kp1= xp1->ToKeyParts, kp2= xp2->ToKeyParts; + b && (kp1 || kp2); + kp1= kp1->Next, kp2= kp2->Next) + if (!kp1 || !kp2) + b= false; + else if (stricmp(kp1->Name, kp2->Name)) + b= false; + else if (kp1->Klen != kp2->Klen) + b= false; + + return b; +} // end of IsSameIndex + +/** + @brief + This create a lock on the table. If you are implementing a storage engine + that can handle transacations look at ha_berkely.cc to see how you will + want to go about doing this. Otherwise you should consider calling flock() + here. Hint: Read the section "locking functions for mysql" in lock.cc to understand + this. + + @details + Called from lock.cc by lock_external() and unlock_external(). Also called + from sql_table.cc by copy_data_between_tables(). + + @note + Following what we did in the MySQL XDB handler, we use this call to actually + physically open the table. This could be reconsider when finalizing this handler + design, which means we have a better understanding of what MariaDB does. + + @see + lock.cc by lock_external() and unlock_external() in lock.cc; + the section "locking functions for mysql" in lock.cc; + copy_data_between_tables() in sql_table.cc. +*/ +int ha_connect::external_lock(THD *thd, int lock_type) +{ + int rc= 0; + bool del= false, xcheck=false, cras= false; + MODE newmode; + PTOS options= GetTableOptionStruct(table); + PGLOBAL g= GetPlug(thd); + DBUG_ENTER("ha_connect::external_lock"); + + if (xtrace) + printf("%p external_lock: lock_type=%d\n", this, lock_type); + + if (!g) + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + if (lock_type != F_UNLCK && check_privileges(thd, options)) { + strcpy(g->Message, "This operation requires the FILE privilege"); + printf("%s\n", g->Message); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif check_privileges + + // Action will depend on lock_type + switch (lock_type) { + case F_WRLCK: + newmode= MODE_WRITE; + break; + case F_RDLCK: + newmode= MODE_READ; + break; + case F_UNLCK: + default: + newmode= MODE_ANY; + } // endswitch mode + + if (newmode == MODE_ANY) { + // This is unlocking, do it by closing the table + if (xp->CheckQueryID()) + rc= 2; // Logical error ??? + else if (g->Xchk) { + if (!tdbp || *tdbp->GetName() == '#') { + bool oldsep= ((PCHK)g->Xchk)->oldsep; + bool newsep= ((PCHK)g->Xchk)->newsep; + PTDBDOS tdp= (PTDBDOS)(tdbp ? tdbp : GetTDB(g)); + PDOSDEF ddp= (PDOSDEF)tdp->GetDef(); + PIXDEF xp, xp1, xp2, drp=NULL, adp= NULL; + PIXDEF oldpix= ((PCHK)g->Xchk)->oldpix; + PIXDEF newpix= ((PCHK)g->Xchk)->newpix; + PIXDEF *xlst, *xprc; + + ddp->SetIndx(oldpix); + + if (oldsep != newsep) { + // All indexes have to be remade + ddp->DeleteIndexFile(g, NULL); + oldpix= NULL; + ddp->SetIndx(NULL); + SetBooleanOption("Sepindex", newsep); + } else if (newsep) { + // Make the list of dropped indexes + xlst= &drp; xprc= &oldpix; + + for (xp2= oldpix; xp2; xp2= xp) { + for (xp1= newpix; xp1; xp1= xp1->Next) + if (IsSameIndex(xp1, xp2)) + break; // Index not to drop + + xp= xp2->GetNext(); + + if (!xp1) { + *xlst= xp2; + *xprc= xp; + *(xlst= &xp2->Next)= NULL; + } else + xprc= &xp2->Next; + + } // endfor xp2 + + if (drp) { + // Here we erase the index files + ddp->DeleteIndexFile(g, drp); + } // endif xp1 + + } else if (oldpix) { + // TODO: optimize the case of just adding new indexes + if (!newpix) + ddp->DeleteIndexFile(g, NULL); + + oldpix= NULL; // To remake all indexes + ddp->SetIndx(NULL); + } // endif sepindex + + // Make the list of new created indexes + xlst= &adp; xprc= &newpix; + + for (xp1= newpix; xp1; xp1= xp) { + for (xp2= oldpix; xp2; xp2= xp2->Next) + if (IsSameIndex(xp1, xp2)) + break; // Index already made + + xp= xp1->Next; + + if (!xp2) { + *xlst= xp1; + *xprc= xp; + *(xlst= &xp1->Next)= NULL; + } else + xprc= &xp1->Next; + + } // endfor xp1 + + if (adp) + // Here we do make the new indexes + tdp->MakeIndex(g, adp, true); + + } // endif Mode + + } // endelse Xchk + + if (CloseTable(g)) + rc= HA_ERR_INTERNAL_ERROR; + + DBUG_RETURN(rc); + } // endif MODE_ANY + + if (xtrace) { + printf("%p external_lock: cmdtype=%d\n", this, thd->lex->sql_command); + printf("Cmd=%.*s\n", (int) thd->query_string.length(), + thd->query_string.str()); + } // endif xtrace + + // Next code is temporarily replaced until sql_command is set + stop= false; + + if (newmode == MODE_WRITE) { + switch (thd->lex->sql_command) { + case SQLCOM_CREATE_TABLE: + case SQLCOM_INSERT: + case SQLCOM_LOAD: + case SQLCOM_INSERT_SELECT: + newmode= MODE_INSERT; + break; +// case SQLCOM_REPLACE: +// case SQLCOM_REPLACE_SELECT: +// newmode= MODE_UPDATE; // To be checked +// break; + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + del= true; + case SQLCOM_TRUNCATE: + newmode= MODE_DELETE; + break; + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + newmode= MODE_UPDATE; + break; + case SQLCOM_SELECT: + case SQLCOM_OPTIMIZE: + newmode= MODE_READ; + break; + case SQLCOM_DROP_TABLE: + case SQLCOM_RENAME_TABLE: + case SQLCOM_ALTER_TABLE: + newmode= MODE_ANY; + break; + case SQLCOM_DROP_INDEX: + case SQLCOM_CREATE_INDEX: + newmode= MODE_ANY; +// stop= true; + break; + case SQLCOM_CREATE_VIEW: + case SQLCOM_DROP_VIEW: + newmode= MODE_ANY; + break; + default: + printf("Unsupported sql_command=%d", thd->lex->sql_command); + sprintf(g->Message, "Unsupported sql_command=%d", thd->lex->sql_command); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endswitch newmode + + } else if (newmode == MODE_READ) { + switch (thd->lex->sql_command) { + case SQLCOM_CREATE_TABLE: + xcheck= true; + cras= true; + case SQLCOM_INSERT: + case SQLCOM_LOAD: + case SQLCOM_INSERT_SELECT: +// case SQLCOM_REPLACE: +// case SQLCOM_REPLACE_SELECT: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_TRUNCATE: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_SELECT: + case SQLCOM_OPTIMIZE: + break; + case SQLCOM_DROP_INDEX: + case SQLCOM_CREATE_INDEX: + case SQLCOM_ALTER_TABLE: + xcheck= true; +// stop= true; + case SQLCOM_DROP_TABLE: + case SQLCOM_RENAME_TABLE: + newmode= MODE_ANY; + break; + case SQLCOM_CREATE_VIEW: + case SQLCOM_DROP_VIEW: + newmode= MODE_ANY; + break; + default: + printf("Unsupported sql_command=%d", thd->lex->sql_command); + sprintf(g->Message, "Unsupported sql_command=%d", thd->lex->sql_command); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endswitch newmode + + } // endif's newmode + + + if (xtrace) + printf("New mode=%d\n", newmode); + + // If this is the start of a new query, cleanup the previous one + if (xp->CheckCleanup()) { + tdbp= NULL; + valid_info= false; + } // endif CheckCleanup + + if (xcheck) { + // This must occur after CheckCleanup + g->Xchk= new(g) XCHK; + ((PCHK)g->Xchk)->oldsep= GetBooleanOption("Sepindex", false); + ((PCHK)g->Xchk)->oldpix= GetIndexInfo(); + } // endif xcheck + + if (cras) + g->Createas= 1; // To tell created table to ignore FLAG + + if (xtrace) + printf("Calling CntCheckDB db=%s\n", GetDBName(NULL)); + + // Set or reset the good database environment + if (CntCheckDB(g, this, GetDBName(NULL))) { + printf("%p external_lock: %s\n", this, g->Message); + rc= HA_ERR_INTERNAL_ERROR; + // This can NOT be called without open called first, but + // the table can have been closed since then + } else if (!tdbp || xp->CheckQuery(valid_query_id) || xmod != newmode) { + if (tdbp) + CloseTable(g); + + xmod= newmode; + + if (!table) + rc= 3; // Logical error + + // Delay open until used fields are known + } // endif tdbp + + if (xtrace) + printf("external_lock: rc=%d\n", rc); + + DBUG_RETURN(rc); +} // end of external_lock + + +/** + @brief + The idea with handler::store_lock() is: The statement decides which locks + should be needed for the table. For updates/deletes/inserts we get WRITE + locks, for SELECT... we get read locks. + + @details + Before adding the lock into the table lock handler (see thr_lock.c), + mysqld calls store lock with the requested locks. Store lock can now + modify a write lock to a read lock (or some other lock), ignore the + lock (if we don't want to use MySQL table locks at all), or add locks + for many tables (like we do when we are using a MERGE handler). + + Berkeley DB, for example, changes all WRITE locks to TL_WRITE_ALLOW_WRITE + (which signals that we are doing WRITES, but are still allowing other + readers and writers). + + When releasing locks, store_lock() is also called. In this case one + usually doesn't have to do anything. + + In some exceptional cases MySQL may send a request for a TL_IGNORE; + This means that we are requesting the same lock as last time and this + should also be ignored. (This may happen when someone does a flush + table when we have opened a part of the tables, in which case mysqld + closes and reopens the tables and tries to get the same locks at last + time). In the future we will probably try to remove this. + + Called from lock.cc by get_lock_data(). + + @note + In this method one should NEVER rely on table->in_use, it may, in fact, + refer to a different thread! (this happens if get_lock_data() is called + from mysql_lock_abort_for_thread() function) + + @see + get_lock_data() in lock.cc +*/ +THR_LOCK_DATA **ha_connect::store_lock(THD *thd, + THR_LOCK_DATA **to, + enum thr_lock_type lock_type) +{ + if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) + lock.type=lock_type; + *to++ = &lock; + return to; +} + + +/** + @brief + Used to delete or rename a table. By the time delete_table() has been + called all opened references to this table will have been closed + (and your globally shared references released) ===> too bad!!! + The variable name will just be the name of the table. + You will need to remove or rename any files you have created at + this point. + + @details + If you do not implement this, the default delete_table() is called from + handler.cc and it will delete all files with the file extensions returned + by bas_ext(). + + Called from handler.cc by delete_table and ha_create_table(). Only used + during create if the table_flag HA_DROP_BEFORE_CREATE was specified for + the storage engine. + + @see + delete_table and ha_create_table() in handler.cc +*/ +int ha_connect::delete_or_rename_table(const char *name, const char *to) +{ + DBUG_ENTER("ha_connect::delete_or_rename_table"); + /* We have to retrieve the information about this table options. */ + ha_table_option_struct *pos; +#if defined(WIN32) + const char *fmt= ".\\%[^\\]\\%s"; +#else // !WIN32 + const char *fmt= "./%[^/]/%s"; +#endif // !WIN32 + char key[MAX_DBKEY_LENGTH], db[128], tabname[128]; + int rc; + uint key_length, db_flags= 0; + TABLE_LIST table_list; + TABLE_SHARE *share; + THD *thd= current_thd; + + if (to) + if (sscanf(to, fmt, db, tabname) != 2 || *tabname == '#') + goto fin; + + if (sscanf(name, fmt, db, tabname) != 2 || *tabname == '#') + goto fin; + + table_list.db= (char*) db; + table_list.table_name= (char*) tabname; + key_length= create_table_def_key(thd, key, &table_list, 0); + + // share contains the option struct that we need + if (!(share= alloc_table_share(&table_list, key, key_length))) + goto fin; + + // Get the share info from the .frm file + if (open_table_def(thd, share, db_flags)) + goto err; + + // Now we can work + pos= share->option_struct; + + if (check_privileges(thd, pos)) + { + free_table_share(share); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + if (IsFileType(GetTypeID(pos->type)) && !pos->filename) { + // This is a table whose files must be erased or renamed */ +// char ftype[8], *new_exts[2]; + char ftype[12], *xtype, *new_exts[3]; + int n= 0; + + if (share->keynames.count) { + switch (GetTypeID(pos->type)) { + case TAB_CSV: + case TAB_FMT: + case TAB_DOS: xtype= ".dnx"; break; + case TAB_FIX: xtype= ".fnx"; break; + case TAB_BIN: xtype= ".bnx"; break; + case TAB_VEC: xtype= ".vnx"; break; + case TAB_DBF: xtype= ".dbx"; break; + default: + xtype= NULL; +// return true; + } // endswitch Ftype + + if (xtype) + new_exts[n++]= xtype; + + } // endif keynames + + // Fold type to lower case + ftype[0]= '.'; + + for (int i= 0; i < 12; i++) + if (!pos->type[i]) { + ftype[i+1]= 0; + break; + } else + ftype[i+1]= tolower(pos->type[i]); + + new_exts[n++]= ftype; + new_exts[n]= NULL; + + // This will be answered by bas_ext() + ha_connect_exts= (const char**)new_exts; + + // Let the base handler do the job + if (to) + rc= handler::rename_table(name, to); + else + rc= handler::delete_table(name); + + // Reset the ext list to null. + ha_connect_exts= ha_connect_null_exts; + } // endif filename + + // Done no more need for this + err: + free_table_share(share); + fin: + DBUG_RETURN(0); +} // end of delete_or_rename_table + +int ha_connect::delete_table(const char *name) +{ + return delete_or_rename_table(name, NULL); +} // end of delete_table + +int ha_connect::rename_table(const char *from, const char *to) +{ + return delete_or_rename_table(from, to); +} // end of rename_table + +/** + @brief + Given a starting key and an ending key, estimate the number of rows that + will exist between the two keys. + + @details + end_key may be empty, in which case determine if start_key matches any rows. + + Called from opt_range.cc by check_quick_keys(). + + @see + check_quick_keys() in opt_range.cc +*/ +ha_rows ha_connect::records_in_range(uint inx, key_range *min_key, + key_range *max_key) +{ + ha_rows rows; + DBUG_ENTER("ha_connect::records_in_range"); + + if (indexing < 0 || inx != active_index) + index_init(inx, false); + + if (xtrace) + printf("records_in_range: inx=%d indexing=%d\n", inx, indexing); + + if (indexing > 0) { + int nval; + uint len[2]; + const uchar *key[2]; + bool incl[2]; + key_part_map kmap[2]; + + key[0]= (min_key) ? min_key->key : NULL; + key[1]= (max_key) ? max_key->key : NULL; + len[0]= (min_key) ? min_key->length : 0; + len[1]= (max_key) ? max_key->length : 0; + incl[0]= (min_key) ? (min_key->flag == HA_READ_KEY_EXACT) : false; + incl[1]= (max_key) ? (max_key->flag == HA_READ_AFTER_KEY) : false; + kmap[0]= (min_key) ? min_key->keypart_map : 0; + kmap[1]= (max_key) ? max_key->keypart_map : 0; + + if ((nval= CntIndexRange(xp->g, tdbp, key, len, incl, kmap)) < 0) + rows= HA_POS_ERROR; + else + rows= (ha_rows)nval; + + } else if (indexing < 0) + rows= HA_POS_ERROR; + else + rows= 100000000; // Don't use missing index + + DBUG_RETURN(rows); +} // end of records_in_range + +#if defined(MARIADB) +/** + Convert an ISO-8859-1 column name to UTF-8 +*/ +char *ha_connect::encode(PGLOBAL g, char *cnm) + { + char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) * 3); + uint dummy_errors; + uint32 len= copy_and_convert(buf, strlen(cnm) * 3, + &my_charset_utf8_general_ci, + cnm, strlen(cnm), + &my_charset_latin1, + &dummy_errors); + buf[len]= '\0'; + return buf; + } // end of Encode + +/** + Store field definition for create. + + @return + Return 0 if ok +*/ + +bool ha_connect::add_fields(THD *thd, void *alt_info, + LEX_STRING *field_name, + enum_field_types type, + char *length, char *decimals, + uint type_modifier, +// Item *default_value, Item *on_update_value, + LEX_STRING *comment, +// char *change, +// List<String> *interval_list, + CHARSET_INFO *cs, +// uint uint_geom_type, + void *vcolinfo, + engine_option_value *create_options) +{ + register Create_field *new_field; + Alter_info *alter_info= (Alter_info*)alt_info; + Virtual_column_info *vcol_info= (Virtual_column_info *)vcolinfo; + + DBUG_ENTER("ha_connect::add_fields"); + + if (check_string_char_length(field_name, "", NAME_CHAR_LEN, + system_charset_info, 1)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } +#if 0 + if (type_modifier & PRI_KEY_FLAG) + { + Key *key; + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::PRIMARY, null_lex_str, + &default_key_create_info, + 0, lex->col_list, NULL); + alter_info->key_list.push_back(key); + lex->col_list.empty(); + } + if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) + { + Key *key; + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::UNIQUE, null_lex_str, + &default_key_create_info, 0, + lex->col_list, NULL); + alter_info->key_list.push_back(key); + lex->col_list.empty(); + } + + if (default_value) + { + /* + Default value should be literal => basic constants => + no need fix_fields() + + We allow only one function as part of default value - + NOW() as default for TIMESTAMP type. + */ + if (default_value->type() == Item::FUNC_ITEM && + !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && + type == MYSQL_TYPE_TIMESTAMP)) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); + DBUG_RETURN(1); + } + else if (default_value->type() == Item::NULL_ITEM) + { + default_value= 0; + if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == + NOT_NULL_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); + DBUG_RETURN(1); + } + } + else if (type_modifier & AUTO_INCREMENT_FLAG) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); + DBUG_RETURN(1); + } + } + + if (on_update_value && type != MYSQL_TYPE_TIMESTAMP) + { + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str); + DBUG_RETURN(1); + } +#endif // 0 + + if (!(new_field= new Create_field()) || + new_field->init(thd, field_name->str, type, length, decimals, type_modifier, + NULL, NULL, comment, NULL, + NULL, cs, 0, vcol_info, + create_options)) + DBUG_RETURN(1); + + alter_info->create_list.push_back(new_field); +//lex->last_field=new_field; + DBUG_RETURN(0); +} // end of add_fields + +/** + @brief + pre_create() is called when creating a table with no columns. + + @details + When pre_create() is called the .frm file have not already been + created. You can overwrite some definitions at this point but the + main purpose of it is to define the columns for some table types. + + @note + Not really implemented yet. +*/ +bool ha_connect::pre_create(THD *thd, HA_CREATE_INFO *create_info, + void *alt_info) +{ + char spc= ',', qch= 0; + const char *typn= "?"; + const char *fncn= "?"; + const char *user; + char *fn, *dsn, *tab, *db, *host, *pwd, *prt, *sep; // *csn; +#if defined(WIN32) + char *nsp= NULL, *cls= NULL; +#endif // WIN32 + int port= MYSQL_PORT, hdr= 0, mxr= 0; + uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL); + bool b= false, ok= false, dbf= false; + TABTYPE ttp= TAB_UNDEF; + LEX_STRING *comment, *name, *val; + MEM_ROOT *mem= thd->mem_root; + CHARSET_INFO *cs; + Alter_info *alter_info= (Alter_info*)alt_info; + engine_option_value *pov, **start= &create_info->option_list, *end= NULL; + PQRYRES qrp; + PCOLRES crp; + PGLOBAL g= GetPlug(thd); + + if (!g) + return true; + + fn= dsn= tab= db= host= pwd= prt= sep= NULL; + user= NULL; + + // Get the useful create options + for (pov= *start; pov; pov= pov->next) { + if (!stricmp(pov->name.str, "table_type")) { + typn= pov->value.str; + ttp= GetTypeID(typn); + } else if (!stricmp(pov->name.str, "file_name")) { + fn= pov->value.str; + } else if (!stricmp(pov->name.str, "tabname")) { + tab= pov->value.str; + } else if (!stricmp(pov->name.str, "dbname")) { + db= pov->value.str; + } else if (!stricmp(pov->name.str, "catfunc")) { + fncn= pov->value.str; + fnc= GetFuncID(fncn); + } else if (!stricmp(pov->name.str, "sep_char")) { + sep= pov->value.str; + spc= (!strcmp(sep, "\\t")) ? '\t' : *sep; + } else if (!stricmp(pov->name.str, "qchar")) { + qch= *pov->value.str; + } else if (!stricmp(pov->name.str, "quoted")) { + if (!qch) + qch= '"'; + + } else if (!stricmp(pov->name.str, "header")) { + hdr= atoi(pov->value.str); + } else if (!stricmp(pov->name.str, "option_list")) { + host= GetListOption("host", pov->value.str, "localhost"); + user= GetListOption("user", pov->value.str, "root"); + // Default value db can come from the DBNAME=xxx option. + db= GetListOption("database", pov->value.str, db); + pwd= GetListOption("password", pov->value.str); + prt= GetListOption("port", pov->value.str); + port= (prt) ? atoi(prt) : MYSQL_PORT; +#if defined(WIN32) + nsp= GetListOption("namespace", pov->value.str); + cls= GetListOption("class", pov->value.str); +#endif // WIN32 + mxr= atoi(GetListOption("maxerr", pov->value.str, "0")); + } // endelse option_list + + end= pov; + } // endfor pov + + if (!db) + db= thd->db; // Default value + + // Check table type + if (ttp == TAB_UNDEF) { + strcpy(g->Message, "No table_type. Was set to DOS"); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + ttp= TAB_DOS; + typn= "DOS"; + name= thd->make_lex_string(NULL, "table_type", 10, true); + val= thd->make_lex_string(NULL, typn, strlen(typn), true); + pov= new(mem) engine_option_value(*name, *val, false, start, &end); + } else if (ttp == TAB_NIY) { + sprintf(g->Message, "Unsupported table type %s", typn); + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + return true; + } // endif ttp + + if (!tab && !(fnc & (FNC_TABLE | FNC_COL))) + tab= (char*)create_info->alias; + + switch (ttp) { +#if defined(ODBC_SUPPORT) + case TAB_ODBC: + if (!(dsn= create_info->connect_string.str) + && !(fnc & (FNC_DSN | FNC_DRIVER))) + sprintf(g->Message, "Missing %s connection string", typn); + else + ok= true; + + supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER); + break; +#endif // ODBC_SUPPORT + case TAB_DBF: + dbf= true; + // Passthru + case TAB_CSV: + if (!fn && fnc != FNC_NO) + sprintf(g->Message, "Missing %s file name", typn); + else + ok= true; + + break; +#if defined(MYSQL_SUPPORT) + case TAB_MYSQL: + ok= true; + + if ((dsn= create_info->connect_string.str)) { + PMYDEF mydef= new(g) MYSQLDEF(); + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; + + dsn= (char*)PlugSubAlloc(g, NULL, strlen(dsn) + 1); + strncpy(dsn, create_info->connect_string.str, + create_info->connect_string.length); + dsn[create_info->connect_string.length] = 0; + mydef->Name= (char*)create_info->alias; + mydef->Cat= cat; + + if (!mydef->ParseURL(g, dsn)) { + host= mydef->Hostname; + user= mydef->Username; + pwd= mydef->Password; + db= mydef->Database; + tab= mydef->Tabname; + port= mydef->Portnumber; + } else + ok= false; + + } else if (!user) + user= "root"; // Avoid crash + + break; +#endif // MYSQL_SUPPORT +#if defined(WIN32) + case TAB_WMI: + ok= true; + break; +#endif // WIN32 + default: + sprintf(g->Message, "Cannot get column info for table type %s", typn); + } // endif ttp + + // Check for supported catalog function + if (ok && !(supfnc & fnc)) { + sprintf(g->Message, "Unsupported catalog function %s for table type %s", + fncn, typn); + ok= false; + } // endif supfnc + + // Test whether columns must be specified + if (alter_info->create_list.elements) { + if (g->Createas) { + // This table is created AS SELECT + // The sourcetable FLAG values have been passed to the created + // table columns but they must be removed to get default offsets. + List_iterator<Create_field> it(alter_info->create_list); + Create_field *field; + engine_option_value *vop, *pop; + + while ((field= it++)) + for (pop= NULL, vop= field->option_list; vop; vop= vop->next) + if (!stricmp(vop->name.str, "FLAG")) { + if (pop) + pop->next= vop->next; + else + field->option_list= vop->next; + + break; + } else + pop= vop; + + g->Createas= 0; + } // endif Createas + + return false; + } // endif elements + + if (ok) { + char *length, *decimals, *cnm, *rem; + int i, len, dec, typ; + enum_field_types type; + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; + + if (cat) + cat->SetDataPath(g, thd->db); + else + return true; // Should never happen + + switch (ttp) { + case TAB_DBF: + qrp= DBFColumns(g, fn, fnc == FNC_COL); + break; +#if defined(ODBC_SUPPORT) + case TAB_ODBC: + switch (fnc) { + case FNC_NO: + case FNC_COL: + qrp= ODBCColumns(g, dsn, tab, NULL, fnc == FNC_COL); + break; + case FNC_TABLE: + qrp= ODBCTables(g, dsn, tab, true); + break; + case FNC_DSN: + qrp= ODBCDataSources(g, true); + break; + case FNC_DRIVER: + qrp= ODBCDrivers(g, true); + break; + default: + sprintf(g->Message, "invalid catfunc %s", fncn); + } // endswitch info + + break; +#endif // ODBC_SUPPORT +#if defined(MYSQL_SUPPORT) + case TAB_MYSQL: + qrp= MyColumns(g, host, db, user, pwd, tab, + NULL, port, false, fnc == FNC_COL); + break; +#endif // MYSQL_SUPPORT + case TAB_CSV: + qrp= CSVColumns(g, fn, spc, qch, hdr, mxr, fnc == FNC_COL); + break; +#if defined(WIN32) + case TAB_WMI: + qrp= WMIColumns(g, nsp, cls, fnc == FNC_COL); + break; +#endif // WIN32 + default: + strcpy(g->Message, "System error in pre_create"); + break; + } // endswitch ttp + + if (!qrp) { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + return true; + } // endif qrp + + if (fnc != FNC_NO) { + // Catalog table + for (crp=qrp->Colresp; !b && crp; crp= crp->Next) { + cnm= encode(g, crp->Name); + name= thd->make_lex_string(NULL, cnm, strlen(cnm), true); + type= PLGtoMYSQL(crp->Type, dbf); + len= crp->Length; + length= (char*)PlugSubAlloc(g, NULL, 8); + sprintf(length, "%d", len); + decimals= NULL; + comment= thd->make_lex_string(NULL, "", 0, true); + + // Now add the field + b= add_fields(thd, alt_info, name, type, length, decimals, + NOT_NULL_FLAG, comment, NULL, NULL, NULL); + } // endfor crp + + } else // Not a catalog table + for (i= 0; !b && i < qrp->Nblin; i++) { + rem= ""; + typ= len= dec= 0; + length= ""; + decimals= NULL; + tm= NOT_NULL_FLAG; + cs= NULL; + + for (crp= qrp->Colresp; crp; crp= crp->Next) + switch (crp->Fld) { + case FLD_NAME: + cnm= encode(g, crp->Kdata->GetCharValue(i)); + name= thd->make_lex_string(NULL, cnm, strlen(cnm), true); + break; + case FLD_TYPE: + typ= crp->Kdata->GetIntValue(i); + break; + case FLD_PREC: + len= crp->Kdata->GetIntValue(i); + break; + case FLD_SCALE: + if ((dec= crp->Kdata->GetIntValue(i))) { + decimals= (char*)PlugSubAlloc(g, NULL, 8); + sprintf(decimals, "%d", dec); + } else + decimals= NULL; + + break; + case FLD_NULL: + if (crp->Kdata->GetIntValue(i)) + tm= 0; // Nullable + + break; + case FLD_REM: + rem= crp->Kdata->GetCharValue(i); + break; +// case FLD_CHARSET: + // No good because remote table is already translated +// if (*(csn= crp->Kdata->GetCharValue(i))) +// cs= get_charset_by_name(csn, 0); + +// break; + default: + break; // Ignore + } // endswitch Fld + +#if defined(ODBC_SUPPORT) + if (ttp == TAB_ODBC) { + int plgtyp; + + // typ must be PLG type, not SQL type + if (!(plgtyp= TranslateSQLType(typ, dec, len))) { + sprintf(g->Message, "Unsupported SQL type %d", typ); + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + return true; + } else + typ= plgtyp; + + // Some data sources do not count dec in length + if (typ == TYPE_FLOAT) + len += (dec + 2); // To be safe + + } // endif ttp +#endif // ODBC_SUPPORT + + // Make the arguments as required by add_fields + type= PLGtoMYSQL(typ, true); + length= (char*)PlugSubAlloc(g, NULL, 8); + sprintf(length, "%d", len); + comment= thd->make_lex_string(NULL, rem, strlen(rem), true); + + // Now add the field + b= add_fields(thd, alt_info, name, type, length, decimals, + tm, comment, cs, NULL, NULL); + } // endfor i + + return b; + } // endif ok + + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + return true; +} // end of pre_create +#endif // MARIADB + +/** + @brief + create() is called to create a database. The variable name will have the name + of the table. + + @details + When create() is called you do not need to worry about + opening the table. Also, the .frm file will have already been + created so adjusting create_info is not necessary. You can overwrite + the .frm file at this point if you wish to change the table + definition, but there are no methods currently provided for doing + so. + + Called from handle.cc by ha_create_table(). + + @note + Currently we do some checking on the create definitions and stop + creating if an error is found. We wish we could change the table + definition such as providing a default table type. However, as said + above, there are no method to do so. + + @see + ha_create_table() in handle.cc +*/ + +int ha_connect::create(const char *name, TABLE *table_arg, + HA_CREATE_INFO *create_info) +{ + int rc= RC_OK; + bool dbf; + Field* *field; + Field *fp; + TABTYPE type; + TABLE *st= table; // Probably unuseful + PGLOBAL g= GetPlug(table_arg->in_use); + + DBUG_ENTER("ha_connect::create"); + PTOS options= GetTableOptionStruct(table_arg); + + // CONNECT engine specific table options: + DBUG_ASSERT(options); + type= GetTypeID(options->type); + + if (check_privileges(current_thd, options)) + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + if (options->data_charset) { + const CHARSET_INFO *data_charset; + + if (!(data_charset= get_charset_by_csname(options->data_charset, + MY_CS_PRIMARY, MYF(0)))) { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), options->data_charset); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif charset + + if (type == TAB_XML && data_charset != &my_charset_utf8_general_ci) { + my_printf_error(ER_UNKNOWN_ERROR, + "DATA_CHARSET='%s' is not supported for TABLE_TYPE=XML", + MYF(0), options->data_charset); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif utf8 + + } // endif charset + + if (!g) { + rc= HA_ERR_INTERNAL_ERROR; + DBUG_RETURN(rc); + } else + dbf= (GetTypeID(options->type) == TAB_DBF && !options->catfunc); + + if (type == TAB_XML) { + bool dom; // True: MS-DOM, False libxml2 + char *xsup= GetListOption("Xmlsup", options->oplist, "*"); + + // Note that if no support is specified, the default is MS-DOM + // on Windows and libxml2 otherwise + switch (*xsup) { + case '*': +#if defined(WIN32) + dom= true; +#else // !WIN32 + dom= false; +#endif // !WIN32 + break; + case 'M': + case 'D': + dom= true; + break; + default: + dom= false; + } // endswitch xsup + +#if !defined(DOMDOC_SUPPORT) + if (dom) { + strcpy(g->Message, "MS-DOM not supported by this version"); + xsup= NULL; + } // endif DomDoc +#endif // !DOMDOC_SUPPORT + +#if !defined(LIBXML2_SUPPORT) + if (!dom) { + strcpy(g->Message, "libxml2 not supported by this version"); + xsup= NULL; + } // endif Libxml2 +#endif // !LIBXML2_SUPPORT + + if (!xsup) { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + rc= HA_ERR_INTERNAL_ERROR; + DBUG_RETURN(rc); + } // endif xsup + + } // endif type + + // Check column types + for (field= table_arg->field; *field; field++) { + fp= *field; + +#if defined(MARIADB) + if (fp->vcol_info && !fp->stored_in_db) + continue; // This is a virtual column +#endif // MARIADB + + if (fp->flags & AUTO_INCREMENT_FLAG) { + strcpy(g->Message, "Auto_increment is not supported yet"); + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + rc= HA_ERR_INTERNAL_ERROR; + DBUG_RETURN(rc); + } // endif flags + + switch (fp->type()) { + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_TINY: + break; // Ok + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_INT24: + break; // To be checked + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + default: +// fprintf(stderr, "Unsupported type column %s\n", fp->field_name); + sprintf(g->Message, "Unsupported type for column %s", + fp->field_name); + rc= HA_ERR_INTERNAL_ERROR; + my_printf_error(ER_UNKNOWN_ERROR, + "Unsupported type for column '%s'", + MYF(0), fp->field_name); + DBUG_RETURN(rc); + } // endswitch type + + if ((fp)->real_maybe_null() && !IsTypeNullable(type)) { + my_printf_error(ER_UNKNOWN_ERROR, + "Table type %s does not support nullable columns", + MYF(0), options->type); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } // endif !nullable + + if (dbf) { + bool b= false; + + if ((b= strlen(fp->field_name) > 10)) + sprintf(g->Message, "DBF: Column name '%s' is too long (max=10)", + fp->field_name); + else if ((b= fp->field_length > 255)) + sprintf(g->Message, "DBF: Column length too big for '%s' (max=255)", + fp->field_name); + + if (b) { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + rc= HA_ERR_INTERNAL_ERROR; + DBUG_RETURN(rc); + } // endif b + + } // endif dbf + + } // endfor field + + if (IsFileType(type)) { + table= table_arg; // Used by called functions + + if (!options->filename) { + // The file name is not specified, create a default file in + // the database directory named table_name.table_type. + // (temporarily not done for XML because a void file causes + // the XML parsers to report an error on the first Insert) + char buf[256], fn[_MAX_PATH], dbpath[128], lwt[12]; + int h; + + strcpy(buf, GetTableName()); + + if (*buf != '#') { + // Check for incompatible options + if (options->sepindex) { + my_message(ER_UNKNOWN_ERROR, + "SEPINDEX is incompatible with unspecified file name", + MYF(0)); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } else if (GetTypeID(options->type) == TAB_VEC) + if (!table->s->max_rows || options->split) { + my_printf_error(ER_UNKNOWN_ERROR, + "%s tables whose file name is unspecified cannot be split", + MYF(0), options->type); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } else if (options->header == 2) { + my_printf_error(ER_UNKNOWN_ERROR, + "header=2 is not allowed for %s tables whose file name is unspecified", + MYF(0), options->type); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } // endif's + + // Fold type to lower case + for (int i= 0; i < 12; i++) + if (!options->type[i]) { + lwt[i]= 0; + break; + } else + lwt[i]= tolower(options->type[i]); + + strcat(strcat(buf, "."), lwt); + sprintf(g->Message, "No file name. Table will use %s", buf); + push_warning(table->in_use, + MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/"); + PlugSetPath(fn, buf, dbpath); + + if ((h= ::open(fn, O_CREAT | O_EXCL, 0666)) == -1) { + if (errno == EEXIST) + sprintf(g->Message, "Default file %s already exists", fn); + else + sprintf(g->Message, "Error %d creating file %s", errno, fn); + + push_warning(table->in_use, + MYSQL_ERROR::WARN_LEVEL_WARN, 0, g->Message); + } else + ::close(h); + + if (type == TAB_FMT || options->readonly) + push_warning(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "Congratulation, you just created a read-only void table!"); + + } // endif buf + + } // endif filename + + // To check whether indexes have to be made or remade + if (!g->Xchk) { + PIXDEF xdp; + + // We should be in CREATE TABLE + if (table->in_use->lex->sql_command != SQLCOM_CREATE_TABLE) + push_warning(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "Wrong command in create, please contact CONNECT team"); + + // Get the index definitions + if (xdp= GetIndexInfo()) { + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; + + if (cat) { + cat->SetDataPath(g, table_arg->in_use->db); + + if ((rc= optimize(table->in_use, NULL))) { + printf("Create rc=%d %s\n", rc, g->Message); + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + rc= HA_ERR_INTERNAL_ERROR; + } else + CloseTable(g); + + } // endif cat + + } // endif xdp + + } else { + ((PCHK)g->Xchk)->newsep= GetBooleanOption("Sepindex", false); + ((PCHK)g->Xchk)->newpix= GetIndexInfo(); + } // endif Xchk + + table= st; + } // endif type + + DBUG_RETURN(rc); +} // end of create + + +/** + check_if_incompatible_data() called if ALTER TABLE can't detect otherwise + if new and old definition are compatible + + @details If there are no other explicit signs like changed number of + fields this function will be called by compare_tables() + (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm + file. + +*/ + +bool ha_connect::check_if_incompatible_data(HA_CREATE_INFO *info, + uint table_changes) +{ +//ha_table_option_struct *param_old, *param_new; + DBUG_ENTER("ha_connect::check_if_incompatible_data"); + // TO DO: really implement and check it. + THD *thd= current_thd; + + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "The current version of CONNECT did not check what you changed in ALTER. Use at your own risk"); + + if (table) { + PTOS newopt= info->option_struct; + PTOS oldopt= table->s->option_struct; + +#if 0 + if (newopt->sepindex != oldopt->sepindex) { + // All indexes to be remade + PGLOBAL g= GetPlug(thd); + + if (!g) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "Execute OPTIMIZE TABLE to remake the indexes"); + else + g->Xchk= new(g) XCHK; + + } // endif sepindex +#endif // 0 + + if (newopt->filename) + DBUG_RETURN(COMPATIBLE_DATA_NO); + + } // endif table + + DBUG_RETURN(COMPATIBLE_DATA_YES); +} // end of check_if_incompatible_data + + +struct st_mysql_storage_engine connect_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +struct st_mysql_daemon unusable_connect= +{ MYSQL_DAEMON_INTERFACE_VERSION }; + +mysql_declare_plugin(connect) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &connect_storage_engine, + "CONNECT", + "Olivier Bertrand", + "Direct access to external data, including many file formats", + PLUGIN_LICENSE_GPL, + connect_init_func, /* Plugin Init */ + connect_done_func, /* Plugin Deinit */ + 0x0001 /* 0.1 */, + NULL, /* status variables */ + NULL, /* system variables */ + NULL, /* config options */ + 0, /* flags */ +} +mysql_declare_plugin_end; + +#if defined(MARIADB) +maria_declare_plugin(connect) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &connect_storage_engine, + "CONNECT", + "Olivier Bertrand", + "Direct access to external data, including many file formats", + PLUGIN_LICENSE_GPL, + connect_init_func, /* Plugin Init */ + connect_done_func, /* Plugin Deinit */ + 0x0001, /* version number (0.1) */ + NULL, /* status variables */ + NULL, /* system variables */ + "0.1", /* string version */ + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL /* maturity */ +}, +{ + MYSQL_DAEMON_PLUGIN, + &unusable_connect, + "UNUSABLE", + "Olivier Bertrand", + "Unusable Daemon", + PLUGIN_LICENSE_PROPRIETARY, + NULL, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0101, /* version number (1.1) */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.01.00.000" , /* version, as a string */ + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL /* maturity */ +} +maria_declare_plugin_end; +#endif // MARIADB diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h new file mode 100644 index 00000000000..3caf636232f --- /dev/null +++ b/storage/connect/ha_connect.h @@ -0,0 +1,424 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** @file ha_connect.h + + @brief + The ha_connect engine is a prototype storage engine to access external data. + + @see + /sql/handler.h and /storage/connect/ha_connect.cc +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +/****************************************************************************/ +/* Structures used to pass info between CONNECT and ha_connect. */ +/****************************************************************************/ +typedef struct _create_xinfo { + char *Type; /* Retrieved from table comment */ + char *Filename; /* Set if not standard */ + char *IndexFN; /* Set if not standard */ + ulonglong Maxrows; /* Estimated max nb of rows */ + ulong Lrecl; /* Set if not default */ + ulong Elements; /* Number of lines in blocks */ + bool Fixed; /* False for DOS type */ + void *Pcf; /* To list of columns */ + void *Pxdf; /* To list of indexes */ +} CRXINFO, *PCXF; + +typedef struct _xinfo { + ulonglong data_file_length; /* Length of data file */ + ha_rows records; /* Records in table */ + ulong mean_rec_length; /* Physical record length */ + char *data_file_name; /* Physical file name */ +} XINFO, *PXF; + +class XCHK : public BLOCK { +public: + XCHK(void) {oldsep= newsep= false; oldpix= newpix= NULL;} + bool oldsep; // Sepindex before create/alter + bool newsep; // Sepindex after create/alter + PIXDEF oldpix; // The indexes before create/alter + PIXDEF newpix; // The indexes after create/alter +}; // end of class XCHK + +typedef class XCHK *PCHK; +typedef class user_connect *PCONNECT; +typedef struct ha_table_option_struct TOS, *PTOS; +typedef struct ha_field_option_struct FOS, *PFOS; + +/** @brief + CONNECT_SHARE is a structure that will be shared among all open handlers. + This example implements the minimum of what you will probably need. +*/ +typedef struct st_connect_share { + char *table_name; + uint table_name_length, use_count; + mysql_mutex_t mutex; + THR_LOCK lock; +#if !defined(MARIADB) + PTOS table_options; + PFOS field_options; +#endif // !MARIADB +} CONNECT_SHARE; + +typedef class ha_connect *PHC; + +/** @brief + Class definition for the storage engine +*/ +class ha_connect: public handler +{ + THR_LOCK_DATA lock; ///< MySQL lock + CONNECT_SHARE *share; ///< Shared lock info + +public: + ha_connect(handlerton *hton, TABLE_SHARE *table_arg); + ~ha_connect(); + + // CONNECT Implementation + static bool connect_init(void); + static bool connect_end(void); + char *GetStringOption(char *opname, char *sdef= NULL); + PTOS GetTableOptionStruct(TABLE *table_arg); + bool GetBooleanOption(char *opname, bool bdef); + bool SetBooleanOption(char *opname, bool b); + int GetIntegerOption(char *opname); + bool SetIntegerOption(char *opname, int n); + PFOS GetFieldOptionStruct(Field *fp); + void *GetColumnOption(void *field, PCOLINFO pcf); + PIXDEF GetIndexInfo(void); + const char *GetDBName(const char *name); + const char *GetTableName(void); + int GetColNameLen(Field *fp); + char *GetColName(Field *fp); + void AddColName(char *cp, Field *fp); + TABLE *GetTable(void) {return table;} + bool IsSameIndex(PIXDEF xp1, PIXDEF xp2); + + PCONNECT GetUser(THD *thd); + PGLOBAL GetPlug(THD *thd); + PTDB GetTDB(PGLOBAL g); + bool OpenTable(PGLOBAL g, bool del= false); + bool IsOpened(void); + int CloseTable(PGLOBAL g); + int MakeRecord(char *buf); + int ScanRecord(PGLOBAL g, uchar *buf); + int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf); + int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL, + uint key_len= 0); + + /** @brief + The name that will be used for display purposes. + */ + const char *table_type() const {return "CONNECT";} + + /** @brief + The name of the index type that will be used for display. + Don't implement this method unless you really have indexes. + */ + const char *index_type(uint inx) { return "XPLUG"; } + + /** @brief + The file extensions. + */ + const char **bas_ext() const; + + /** @brief + This is a list of flags that indicate what functionality the storage engine + implements. The current table flags are documented in handler.h + */ + ulonglong table_flags() const + { + return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS | + HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS | + HA_NO_COPY_ON_ALTER | +#if defined(MARIADB) + HA_CAN_VIRTUAL_COLUMNS | +#endif // MARIADB + HA_NULL_IN_KEY | HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE); + } + + /** @brief + This is a bitmap of flags that indicates how the storage engine + implements indexes. The current index flags are documented in + handler.h. If you do not implement indexes, just return zero here. + + @details + part is the key part to check. First key part is 0. + If all_parts is set, MySQL wants to know the flags for the combined + index, up to and including 'part'. + */ + ulong index_flags(uint inx, uint part, bool all_parts) const + { + return HA_READ_NEXT | HA_READ_RANGE; + } + + /** @brief + unireg.cc will call max_supported_record_length(), max_supported_keys(), + max_supported_key_parts(), uint max_supported_key_length() + to make sure that the storage engine can handle the data it is about to + send. Return *real* limits of your storage engine here; MySQL will do + min(your_limits, MySQL_limits) automatically. + */ + uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; } + + /** @brief + unireg.cc will call this to make sure that the storage engine can handle + the data it is about to send. Return *real* limits of your storage engine + here; MySQL will do min(your_limits, MySQL_limits) automatically. + + @details + There is no need to implement ..._key_... methods if your engine doesn't + support indexes. + */ + uint max_supported_keys() const { return 10; } + + /** @brief + unireg.cc will call this to make sure that the storage engine can handle + the data it is about to send. Return *real* limits of your storage engine + here; MySQL will do min(your_limits, MySQL_limits) automatically. + + @details + There is no need to implement ..._key_... methods if your engine doesn't + support indexes. + */ + uint max_supported_key_parts() const { return 10; } + + /** @brief + unireg.cc will call this to make sure that the storage engine can handle + the data it is about to send. Return *real* limits of your storage engine + here; MySQL will do min(your_limits, MySQL_limits) automatically. + + @details + There is no need to implement ..._key_... methods if your engine doesn't + support indexes. + */ + uint max_supported_key_length() const { return 255; } + + /** @brief + Called in test_quick_select to determine if indexes should be used. + */ + virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; } + + /** @brief + This method will never be called if you do not implement indexes. + */ + virtual double read_time(uint, uint, ha_rows rows) + { return (double) rows / 20.0+1; } + + /* + Everything below are methods that we implement in ha_connect.cc. + + Most of these methods are not obligatory, skip them and + MySQL will treat them as not implemented + */ + virtual bool get_error_message(int error, String *buf); + + /** + Push condition down to the table handler. + + @param cond Condition to be pushed. The condition tree must not be + modified by the by the caller. + + @return + The 'remainder' condition that caller must use to filter out records. + NULL means the handler will not return rows that do not match the + passed condition. + + @note + The pushed conditions form a stack (from which one can remove the + last pushed condition using cond_pop). + The table handler filters out rows using (pushed_cond1 AND pushed_cond2 + AND ... AND pushed_condN) + or less restrictive condition, depending on handler's capabilities. + + handler->ha_reset() call empties the condition stack. + Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the + condition stack. + */ +virtual const COND *cond_push(const COND *cond); +PFIL CheckCond(PGLOBAL g, PFIL filp, AMT tty, Item *cond); +const char *GetValStr(OPVAL vop, bool neg); + + /** + Number of rows in table. It will only be called if + (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0 + */ + virtual ha_rows records(); + + /** @brief + We implement this in ha_connect.cc; it's a required method. + */ + int open(const char *name, int mode, uint test_if_locked); // required + + /** @brief + We implement this in ha_connect.cc; it's a required method. + */ + int close(void); // required + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ + int write_row(uchar *buf); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ + int update_row(const uchar *old_data, uchar *new_data); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ + int delete_row(const uchar *buf); + + // Added to the connect handler + int index_init(uint idx, bool sorted); + int index_end(); + int index_read(uchar * buf, const uchar * key, uint key_len, + enum ha_rkey_function find_flag); + int index_next_same(uchar *buf, const uchar *key, uint keylen); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ +//int index_read_map(uchar *buf, const uchar *key, +// key_part_map keypart_map, enum ha_rkey_function find_flag); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ + int index_next(uchar *buf); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ +//int index_prev(uchar *buf); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ + int index_first(uchar *buf); + + /** @brief + We implement this in ha_connect.cc. It's not an obligatory method; + skip it and and MySQL will treat it as not implemented. + */ +//int index_last(uchar *buf); + + /** @brief + Unlike index_init(), rnd_init() can be called two consecutive times + without rnd_end() in between (it only makes sense if scan=1). In this + case, the second call should prepare for the new table scan (e.g if + rnd_init() allocates the cursor, the second call should position the + cursor to the start of the table; no need to deallocate and allocate + it again. This is a required method. + */ + int rnd_init(bool scan); //required + int rnd_end(); + int rnd_next(uchar *buf); ///< required + int rnd_pos(uchar *buf, uchar *pos); ///< required + void position(const uchar *record); ///< required + int info(uint); ///< required + int extra(enum ha_extra_function operation); + int external_lock(THD *thd, int lock_type); ///< required + int delete_all_rows(void); + ha_rows records_in_range(uint inx, key_range *min_key, + key_range *max_key); + /** + These methods can be overridden, but their default implementation + provide useful functionality. + */ + int rename_table(const char *from, const char *to); + /** + Delete a table in the engine. Called for base as well as temporary + tables. + */ + int delete_table(const char *name); + /** + Called by delete_table and rename_table + */ + int delete_or_rename_table(const char *from, const char *to); +#if defined(MARIADB) + bool pre_create(THD *thd, HA_CREATE_INFO *crt_info, void *alt_info); +#endif // MARIADB + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); ///< required + bool check_if_incompatible_data(HA_CREATE_INFO *info, + uint table_changes); + + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type); ///< required + int optimize(THD* thd, HA_CHECK_OPT* check_opt); + +protected: + bool check_privileges(THD *thd, PTOS options); + char *GetListOption(const char *opname, const char *oplist, const char *def= NULL); +#if defined(MARIADB) + char *encode(PGLOBAL g, char *cnm); + bool add_fields(THD *thd, void *alter_info, + LEX_STRING *field_name, + enum_field_types type, + char *length, char *decimals, + uint type_modifier, +// Item *default_value, Item *on_update_value, + LEX_STRING *comment, +// char *change, +// List<String> *interval_list, + CHARSET_INFO *cs, +// uint uint_geom_type, + void *vcol_info, + engine_option_value *create_options); +#endif // MARIADB + + // Members + static ulong num; // Tracable handler number + PCONNECT xp; // To user_connect associated class + ulong hnum; // The number of this handler + query_id_t valid_query_id; // The one when tdbp was allocated + query_id_t creat_query_id; // The one when handler was allocated + PTDB tdbp; // To table class object + PVAL sdvalin; // Used to convert date values + PVAL sdvalout; // Used to convert date values + bool istable; // True for table handler +//char tname[64]; // The table name + MODE xmod; // Table mode + XINFO xinfo; // The table info structure + bool valid_info; // True if xinfo is valid + bool stop; // Used when creating index + int indexing; // Type of indexing for CONNECT +#if !defined(MARIADB) + PTOS table_options; + PFOS field_options; +#endif // !MARIADB + THR_LOCK_DATA lock_data; + +public: + TABLE_SHARE *tshp; // Used by called tables + char *data_file_name; + char *index_file_name; + uint int_table_flags; // Inherited from MyISAM + bool enable_activate_all_index; // Inherited from MyISAM +}; // end of ha_connect class definition diff --git a/storage/connect/inihandl.c b/storage/connect/inihandl.c new file mode 100644 index 00000000000..25629b04e60 --- /dev/null +++ b/storage/connect/inihandl.c @@ -0,0 +1,1374 @@ +/* + * Profile functions + * + * Copyright 1993 Miguel de Icaza + * Copyright 1996 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <ctype.h> +//#include <errno.h> +#include <fcntl.h> +//#include <io.h> commented this line out to compile for solaris +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/stat.h> +//#include <sys/types.h> +//#include <memory.h> +#include "my_global.h" +//#include "osutil.h" +#include "global.h" + + +// The types and variables used locally +//typedef int bool; +typedef unsigned int uint; +#define SVP(S) ((S) ? S : "<null>") +#define _strlwr(P) strlwr(P) //OB: changed this line +#define MAX_PATHNAME_LEN 256 +#define N_CACHED_PROFILES 10 +#ifndef WIN32 +#define stricmp strcasecmp +#define _strnicmp strncasecmp +#endif // !WIN32 +#define EnterCriticalSection(x) +#define LeaveCriticalSection(x) + +#if defined(TEST_MODULE) +// Stand alone test program +#include <stdarg.h> + int trace = 0; +void htrc(char const *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf(stderr, fmt, ap); + va_end (ap); +} /* end of htrc */ +#else // !TEST_MODULE +// Normal included functions +extern int trace; +void htrc(char const *fmt, ...); +#endif // !TEST MODULE + + +typedef struct tagPROFILEKEY { + char *value; + struct tagPROFILEKEY *next; + char name[1]; + } PROFILEKEY; + +typedef struct tagPROFILESECTION { + struct tagPROFILEKEY *key; + struct tagPROFILESECTION *next; + char name[1]; + } PROFILESECTION; + +typedef struct { + BOOL changed; + PROFILESECTION *section; +//char *dos_name; +//char *unix_name; + char *filename; + time_t mtime; + } PROFILE; + +#define memfree(P) if (P) free(P) + +/* Cached profile files */ +static PROFILE *MRUProfile[N_CACHED_PROFILES] = {NULL}; + +#define CurProfile (MRUProfile[0]) + +/* wine.ini config file registry root */ +//static HKEY wine_profile_key; + +#define PROFILE_MAX_LINE_LEN 1024 + +/* Wine profile name in $HOME directory; must begin with slash */ +//static const char PROFILE_WineIniName[] = "/.winerc"; + +/* Wine profile: the profile file being used */ +//static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = ""; + +/* Check for comments in profile */ +#define IS_ENTRY_COMMENT(str) ((str)[0] == ';') + +//static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 }; + +//static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect"); + +static const char hex[16] = "0123456789ABCDEF"; + +BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry, + LPCSTR string, LPCSTR filename ); +/*********************************************************************** + * PROFILE_CopyEntry + * + * Copy the content of an entry into a buffer, removing quotes, + * and possibly translating environment variables. + ***********************************************************************/ +static void PROFILE_CopyEntry( char *buffer, const char *value, uint len, + int handle_env ) +{ + const char *p; + char quote = '\0'; + + if (!buffer) + return; + + if ((*value == '\'') || (*value == '\"')) + if (value[1] && (value[strlen(value)-1] == *value)) + quote = *value++; + + if (!handle_env) { + strncpy(buffer, value, len); + + if (quote && (len >= strlen(value))) + buffer[strlen(buffer)-1] = '\0'; + + return; + } // endif handle + + for (p = value; (*p && (len > 1)); *buffer++ = *p++, len--) { + if ((*p == '$') && (p[1] == '{')) { + char env_val[1024]; + const char *env_p; + const char *p2 = strchr(p, '}'); + + if (!p2) + continue; /* ignore it */ + + strncpy(env_val, p + 2, min(sizeof(env_val), (int)(p2-p)-1)); + + if ((env_p = getenv(env_val)) != NULL) { + int buffer_len; + + strncpy( buffer, env_p, len ); + buffer_len = strlen( buffer ); + buffer += buffer_len; + len -= buffer_len; + } // endif env_p + + p = p2 + 1; + } // endif p + + } // endfor p + + if (quote && (len > 1)) + buffer--; + + *buffer = '\0'; +} // end of PROFILE_CopyEntry + + +/*********************************************************************** + * PROFILE_Save + * + * Save a profile tree to a file. + ***********************************************************************/ +static void PROFILE_Save( FILE *file, PROFILESECTION *section ) +{ + PROFILEKEY *key; + int secno; + + for (secno= 0; section; section= section->next) { + if (section->name[0]) { + fprintf(file, "%s[%s]\n", secno ? "\n" : "", SVP(section->name)); + secno++; + } + + for (key = section->key; key; key = key->next) + if (key->name && key->name[0]) { + fprintf(file, "%s", SVP(key->name)); + + if (key->value) + fprintf(file, "=%s", SVP(key->value)); + + fprintf(file, "\n"); + } // endif key->name + + } // endfor section + +} // end of PROFILE_Save + + +/*********************************************************************** + * PROFILE_Free + * + * Free a profile tree. + ***********************************************************************/ +static void PROFILE_Free( PROFILESECTION *section ) +{ + PROFILESECTION *next_section; + PROFILEKEY *key, *next_key; + + for (; section; section = next_section) { + for (key = section->key; key; key = next_key) { + next_key = key->next; + memfree(key->value); + free(key); + } // endfor key + + next_section = section->next; + free(section); + } // endfor section + +} // end of PROFILE_Free + +static int PROFILE_isspace(char c) +{ + /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */ + if (isspace(c) || c=='\r' || c==0x1a) + return 1; + + return 0; +} // end of PROFILE_isspace + + +/*********************************************************************** + * PROFILE_Load + * + * Load a profile tree from a file. + ***********************************************************************/ +static PROFILESECTION *PROFILE_Load( FILE *file ) +{ + char buffer[PROFILE_MAX_LINE_LEN]; + char *p, *p2; + int line = 0; + PROFILESECTION *section, *first_section; + PROFILESECTION* *next_section; + PROFILEKEY *key, *prev_key, **next_key; + + first_section = malloc(sizeof(*section)); + + if (first_section == NULL) + return NULL; + + first_section->name[0] = 0; + first_section->key = NULL; + first_section->next = NULL; + next_section = &first_section->next; + next_key = &first_section->key; + prev_key = NULL; + + while (fgets(buffer, PROFILE_MAX_LINE_LEN, file)) { + line++; + p = buffer; + + while (*p && PROFILE_isspace(*p)) + p++; + + if (*p == '[') { /* section start */ + if (!(p2 = strrchr( p, ']'))) { + fprintf(stderr, "Invalid section header at line %d: '%s'\n", + line, p); + } else { + *p2 = '\0'; + p++; + + if (!(section = malloc(sizeof(*section) + strlen(p)))) + break; + + strcpy(section->name, p); + section->key = NULL; + section->next = NULL; + *next_section = section; + next_section = §ion->next; + next_key = §ion->key; + prev_key = NULL; + + if (trace > 1) + htrc("New section: '%s'\n",section->name); + + continue; + } // endif p2 + + } // endif p + + p2 = p + strlen(p) - 1; + + while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) + *p2-- = '\0'; + + if ((p2 = strchr(p, '=')) != NULL) { + char *p3 = p2 - 1; + + while ((p3 > p) && PROFILE_isspace(*p3)) + *p3-- = '\0'; + + *p2++ = '\0'; + + while (*p2 && PROFILE_isspace(*p2)) + p2++; + + } // endif p2 + + if (*p || !prev_key || *prev_key->name) { + if (!(key = malloc(sizeof(*key) + strlen(p)))) + break; + + strcpy(key->name, p); + + if (p2) { + key->value = malloc(strlen(p2)+1); + strcpy(key->value, p2); + } else + key->value = NULL; + + key->next = NULL; + *next_key = key; + next_key = &key->next; + prev_key = key; + + if (trace > 1) + htrc("New key: name='%s', value='%s'\n", + key->name,key->value?key->value:"(none)"); + + } // endif p || prev_key + + } // endif *p + + return first_section; +} // end of PROFILE_Load + +/*********************************************************************** + * PROFILE_FlushFile + * + * Flush the current profile to disk if changed. + ***********************************************************************/ +static BOOL PROFILE_FlushFile(void) +{ +//char *p, buffer[MAX_PATHNAME_LEN]; +//const char *unix_name; + FILE *file = NULL; + struct stat buf; + + if (trace > 1) + htrc("PROFILE_FlushFile: CurProfile=%p\n", CurProfile); + + if (!CurProfile) { + fprintf(stderr, "No current profile!\n"); + return FALSE; + } // endif !CurProfile + + if (!CurProfile->changed || !CurProfile->filename) + return TRUE; + +#if 0 + if (!(file = fopen(unix_name, "w"))) { + /* Try to create it in $HOME/.wine */ + /* FIXME: this will need a more general solution */ + //strcpy( buffer, get_config_dir() ); + //p = buffer + strlen(buffer); + //*p++ = '/'; + char *p1 = strrchr(CurProfile->filename, '\\'); + + p = buffer; // OB: To be elaborate + + if (p1) + p1++; + else + p1 = CurProfile->dos_name; + + strcpy(p, p1); + _strlwr(p); + file = fopen(buffer, "w"); + unix_name = buffer; + } // endif !unix_name +#endif // 0 + + if (!(file = fopen(CurProfile->filename, "w"))) { + fprintf(stderr, "could not save profile file %s\n", CurProfile->filename); + return FALSE; + } // endif !file + + if (trace > 1) + htrc("Saving '%s'\n", CurProfile->filename); + + PROFILE_Save(file, CurProfile->section); + fclose(file); + CurProfile->changed = FALSE; + + if (!stat(CurProfile->filename, &buf)) + CurProfile->mtime = buf.st_mtime; + + return TRUE; +} // end of PROFILE_FlushFile + + +/*********************************************************************** + * PROFILE_ReleaseFile + * + * Flush the current profile to disk and remove it from the cache. + ***********************************************************************/ +static void PROFILE_ReleaseFile(void) +{ + PROFILE_FlushFile(); + PROFILE_Free(CurProfile->section); +//memfree(CurProfile->dos_name); +//memfree(CurProfile->unix_name); + memfree(CurProfile->filename); + CurProfile->changed = FALSE; + CurProfile->section = NULL; +//CurProfile->dos_name = NULL; +//CurProfile->unix_name = NULL; + CurProfile->filename = NULL; + CurProfile->mtime = 0; +} // end of PROFILE_ReleaseFile + + +/*********************************************************************** + * PROFILE_Open + * + * Open a profile file, checking the cached file first. + ***********************************************************************/ +static BOOL PROFILE_Open(LPCSTR filename) +{ +//char buffer[MAX_PATHNAME_LEN]; +//char *p; + FILE *file = NULL; + int i, j; + struct stat buf; + PROFILE *tempProfile; + + if (trace > 1) + htrc("PROFILE_Open: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES); + + /* First time around */ + if (!CurProfile) + for (i = 0; i < N_CACHED_PROFILES; i++) { + MRUProfile[i] = malloc(sizeof(PROFILE)); + + if (MRUProfile[i] == NULL) + break; + + MRUProfile[i]->changed=FALSE; + MRUProfile[i]->section=NULL; +// MRUProfile[i]->dos_name=NULL; +// MRUProfile[i]->unix_name=NULL; + MRUProfile[i]->filename=NULL; + MRUProfile[i]->mtime=0; + } // endfor i + + /* Check for a match */ + for (i = 0; i < N_CACHED_PROFILES; i++) { + if (trace > 1) + htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i); + + if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) { + if (i) { + PROFILE_FlushFile(); + tempProfile = MRUProfile[i]; + + for (j = i; j > 0; j--) + MRUProfile[j] = MRUProfile[j-1]; + + CurProfile=tempProfile; + } // endif i + + if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime) { + if (trace > 1) + htrc("(%s): already opened (mru=%d)\n", filename, i); + + } else { + if (trace > 1) + htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i); + + } // endif stat + + return TRUE; + } // endif filename + + } // endfor i + + /* Flush the old current profile */ + PROFILE_FlushFile(); + + /* Make the oldest profile the current one only in order to get rid of it */ + if (i == N_CACHED_PROFILES) { + tempProfile = MRUProfile[N_CACHED_PROFILES-1]; + + for(i = N_CACHED_PROFILES-1; i > 0; i--) + MRUProfile[i] = MRUProfile[i-1]; + + CurProfile = tempProfile; + } // endif i + + if (CurProfile->filename) + PROFILE_ReleaseFile(); + + /* OK, now that CurProfile is definitely free we assign it our new file */ +// newdos_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.short_name)+1 ); +// strcpy( newdos_name, full_name.short_name ); + +// newdos_name = malloc(strlen(filename)+1); +// strcpy(newdos_name, filename); + +// CurProfile->dos_name = newdos_name; + CurProfile->filename = malloc(strlen(filename) + 1); + strcpy(CurProfile->filename, filename); + + /* Try to open the profile file, first in $HOME/.wine */ + + /* FIXME: this will need a more general solution */ +// strcpy( buffer, get_config_dir() ); +// p = buffer + strlen(buffer); +// *p++ = '/'; +// strcpy( p, strrchr( newdos_name, '\\' ) + 1 ); +// p = buffer; +// strcpy(p, filename); +// _strlwr(p); + + if (trace > 1) + htrc("Opening %s\n", filename); + + if ((file = fopen(filename, "r"))) { + if (trace > 1) + htrc("(%s): found it\n", filename); + +// CurProfile->unix_name = malloc(strlen(buffer)+1); +// strcpy(CurProfile->unix_name, buffer); + } /* endif file */ + + if (file) { + CurProfile->section = PROFILE_Load(file); + fclose(file); + + if (!stat(CurProfile->filename, &buf)) + CurProfile->mtime = buf.st_mtime; + + } else { + /* Does not exist yet, we will create it in PROFILE_FlushFile */ + fprintf(stderr, "profile file %s not found\n", filename); + } /* endif file */ + + return TRUE; +} + + +/*********************************************************************** + * PROFILE_Close + * + * Flush the named profile to disk and remove it from the cache. + ***********************************************************************/ +void PROFILE_Close(LPCSTR filename) +{ + int i; + BOOL close = FALSE; + struct stat buf; + PROFILE *tempProfile; + + if (trace > 1) + htrc("PROFILE_Close: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES); + + /* Check for a match */ + for (i = 0; i < N_CACHED_PROFILES; i++) { + if (trace > 1) + htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i); + + if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) { + if (i) { + /* Make the profile to close current */ + tempProfile = MRUProfile[i]; + MRUProfile[i] = MRUProfile[0]; + MRUProfile[0] = tempProfile; + CurProfile=tempProfile; + } // endif i + + if (trace > 1) { + if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime) + htrc("(%s): already opened (mru=%d)\n", filename, i); + else + htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i); + + } // endif trace + + close = TRUE; + break; + } // endif filename + + } // endfor i + + if (close) + PROFILE_ReleaseFile(); + +} // end of PROFILE_Close + + +/*********************************************************************** + * PROFILE_DeleteSection + * + * Delete a section from a profile tree. + ***********************************************************************/ +static BOOL PROFILE_DeleteSection(PROFILESECTION* *section, LPCSTR name) +{ + while (*section) { + if ((*section)->name[0] && !stricmp((*section)->name, name)) { + PROFILESECTION *to_del = *section; + + *section = to_del->next; + to_del->next = NULL; + PROFILE_Free(to_del); + return TRUE; + } // endif section + + section = &(*section)->next; + } // endwhile section + + return FALSE; +} // end of PROFILE_DeleteSection + + +/*********************************************************************** + * PROFILE_DeleteKey + * + * Delete a key from a profile tree. + ***********************************************************************/ +static BOOL PROFILE_DeleteKey(PROFILESECTION* *section, + LPCSTR section_name, LPCSTR key_name) +{ + while (*section) { + if ((*section)->name[0] && !stricmp((*section)->name, section_name)) { + PROFILEKEY* *key = &(*section)->key; + + while (*key) { + if (!stricmp((*key)->name, key_name)) { + PROFILEKEY *to_del = *key; + + *key = to_del->next; + memfree(to_del->value); + free(to_del); + return TRUE; + } // endif name + + key = &(*key)->next; + } // endwhile *key + + } // endif section->name + + section = &(*section)->next; + } // endwhile *section + + return FALSE; +} // end of PROFILE_DeleteKey + + +/*********************************************************************** + * PROFILE_DeleteAllKeys + * + * Delete all keys from a profile tree. + ***********************************************************************/ +void PROFILE_DeleteAllKeys(LPCSTR section_name) +{ + PROFILESECTION* *section= &CurProfile->section; + + while (*section) { + if ((*section)->name[0] && !stricmp((*section)->name, section_name)) { + PROFILEKEY* *key = &(*section)->key; + + while (*key) { + PROFILEKEY *to_del = *key; + + *key = to_del->next; + memfree(to_del->value); + free(to_del); + CurProfile->changed = TRUE; + } // endwhile *key + + } // endif section->name + + section = &(*section)->next; + } // endwhile *section + +} // end of PROFILE_DeleteAllKeys + + +/*********************************************************************** + * PROFILE_Find + * + * Find a key in a profile tree, optionally creating it. + ***********************************************************************/ +static PROFILEKEY *PROFILE_Find(PROFILESECTION* *section, + const char *section_name, + const char *key_name, + BOOL create, BOOL create_always) +{ + const char *p; + int seclen, keylen; + + while (PROFILE_isspace(*section_name)) + section_name++; + + p = section_name + strlen(section_name) - 1; + + while ((p > section_name) && PROFILE_isspace(*p)) + p--; + + seclen = p - section_name + 1; + + while (PROFILE_isspace(*key_name)) + key_name++; + + p = key_name + strlen(key_name) - 1; + + while ((p > key_name) && PROFILE_isspace(*p)) + p--; + + keylen = p - key_name + 1; + + while (*section) { + if (((*section)->name[0]) + && (!(_strnicmp((*section)->name, section_name, seclen ))) + && (((*section)->name)[seclen] == '\0')) { + PROFILEKEY* *key = &(*section)->key; + + while (*key) { + /* If create_always is FALSE then we check if the keyname already exists. + * Otherwise we add it regardless of its existence, to allow + * keys to be added more then once in some cases. + */ + if (!create_always) { + if ((!(_strnicmp( (*key)->name, key_name, keylen ))) + && (((*key)->name)[keylen] == '\0')) + return *key; + + } // endif !create_always + + key = &(*key)->next; + } // endwhile *key + + if (!create) + return NULL; + + if (!(*key = malloc(sizeof(PROFILEKEY) + strlen(key_name)))) + return NULL; + + strcpy((*key)->name, key_name); + (*key)->value = NULL; + (*key)->next = NULL; + return *key; + } // endifsection->name + + section = &(*section)->next; + } // endwhile *section + + if (!create) + return NULL; + + *section = malloc(sizeof(PROFILESECTION) + strlen(section_name)); + + if (*section == NULL) + return NULL; + + strcpy((*section)->name, section_name); + (*section)->next = NULL; + + if (!((*section)->key = malloc(sizeof(PROFILEKEY) + strlen(key_name)))) { + free(*section); + return NULL; + } // endif malloc + + strcpy((*section)->key->name, key_name); + (*section)->key->value = NULL; + (*section)->key->next = NULL; + return (*section)->key; +} // end of PROFILE_Find + + +/*********************************************************************** + * PROFILE_GetSection + * + * Returns all keys of a section. + * If return_values is TRUE, also include the corresponding values. + ***********************************************************************/ +static int PROFILE_GetSection(PROFILESECTION *section, LPCSTR section_name, + LPSTR buffer, uint len, + BOOL handle_env, BOOL return_values) +{ + PROFILEKEY *key; + + if(!buffer) + return 0; + + while (section) { + if (section->name[0] && !stricmp(section->name, section_name)) { + uint oldlen = len; + + for (key = section->key; key; key = key->next) { + if (len <= 2) + break; + + if (!*key->name) + continue; /* Skip empty lines */ + + if (IS_ENTRY_COMMENT(key->name)) + continue; /* Skip comments */ + + PROFILE_CopyEntry(buffer, key->name, len - 1, handle_env); + len -= strlen(buffer) + 1; + buffer += strlen(buffer) + 1; + + if (len < 2) + break; + + if (return_values && key->value) { + buffer[-1] = '='; + PROFILE_CopyEntry(buffer, key->value, len - 1, handle_env); + len -= strlen(buffer) + 1; + buffer += strlen(buffer) + 1; + } // endif return_values + + } // endfor key + + *buffer = '\0'; + + if (len <= 1) { + /*If either lpszSection or lpszKey is NULL and the supplied + destination buffer is too small to hold all the strings, + the last string is truncated and followed by two null characters. + In this case, the return value is equal to cchReturnBuffer + minus two. */ + buffer[-1] = '\0'; + return oldlen - 2; + } // endif len + + return oldlen - len; + } // endif section->name + + section = section->next; + } // endwhile section + + buffer[0] = buffer[1] = '\0'; + return 0; +} // end of PROFILE_GetSection + + +/* See GetPrivateProfileSectionNamesA for documentation */ +static int PROFILE_GetSectionNames(LPSTR buffer, uint len) +{ + LPSTR buf; + uint f,l; + PROFILESECTION *section; + + if (trace > 1) + htrc("GetSectionNames: buffer=%p len=%u\n", buffer, len); + + if (!buffer || !len) + return 0; + + if (len == 1) { + *buffer='\0'; + return 0; + } // endif len + + f = len - 1; + buf = buffer; + section = CurProfile->section; + + if (trace > 1) + htrc("GetSectionNames: section=%p\n", section); + + while (section != NULL) { + if (trace > 1) + htrc("section=%s\n", section->name); + + if (section->name[0]) { + l = strlen(section->name) + 1; + + if (trace > 1) + htrc("l=%u f=%u\n", l, f); + + if (l > f) { + if (f > 0) { + strncpy(buf, section->name, f-1); + buf += f-1; + *buf++='\0'; + } // endif f + + *buf = '\0'; + return len - 2; + } // endif l + + strcpy(buf, section->name); + buf += l; + f -= l; + } // endif section->name + + section = section->next; + } // endwhile section + + *buf='\0'; + return buf-buffer; +} // end of PROFILE_GetSectionNames + + +/*********************************************************************** + * PROFILE_GetString + * + * Get a profile string. + * + * Tests with GetPrivateProfileString16, W95a, + * with filled buffer ("****...") and section "set1" and key_name "1" valid: + * section key_name def_val res buffer + * "set1" "1" "x" 43 [data] + * "set1" "1 " "x" 43 [data] (!) + * "set1" " 1 "' "x" 43 [data] (!) + * "set1" "" "x" 1 "x" + * "set1" "" "x " 1 "x" (!) + * "set1" "" " x " 3 " x" (!) + * "set1" NULL "x" 6 "1\02\03\0\0" + * "set1" "" "x" 1 "x" + * NULL "1" "x" 0 "" (!) + * "" "1" "x" 1 "x" + * NULL NULL "" 0 "" + * + *************************************************************************/ +static int PROFILE_GetString(LPCSTR section, LPCSTR key_name, + LPCSTR def_val, LPSTR buffer, uint len) +{ + PROFILEKEY *key = NULL; + + if(!buffer) + return 0; + + if (!def_val) + def_val = ""; + + if (key_name && key_name[0]) { + key = PROFILE_Find(&CurProfile->section, section, key_name, FALSE, FALSE); + PROFILE_CopyEntry(buffer, (key && key->value) ? key->value : def_val, len, FALSE); + + if (trace > 1) + htrc("('%s','%s','%s'): returning '%s'\n", + section, key_name, def_val, buffer ); + + return strlen(buffer); + } // endif key_name + + if (key_name && !(key_name[0])) + /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */ + return 0; + + if (section && section[0]) + return PROFILE_GetSection(CurProfile->section, section, buffer, len, + FALSE, FALSE); + buffer[0] = '\0'; + return 0; +} // end of PROFILE_GetString + + +/*********************************************************************** + * PROFILE_SetString + * + * Set a profile string. + ***********************************************************************/ +static BOOL PROFILE_SetString(LPCSTR section_name, LPCSTR key_name, + LPCSTR value, BOOL create_always) +{ + if (!key_name) { /* Delete a whole section */ + if (trace > 1) + htrc("Deleting('%s')\n", section_name); + + CurProfile->changed |= PROFILE_DeleteSection(&CurProfile->section, + section_name); + return TRUE; /* Even if PROFILE_DeleteSection() has failed, + this is not an error on application's level.*/ + } else if (!value) { /* Delete a key */ + if (trace > 1) + htrc("Deleting('%s','%s')\n", section_name, key_name); + + CurProfile->changed |= PROFILE_DeleteKey(&CurProfile->section, + section_name, key_name); + return TRUE; /* same error handling as above */ + } else { /* Set the key value */ + PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name, + key_name, TRUE, create_always); + if (trace > 1) + htrc("Setting('%s','%s','%s')\n", section_name, key_name, value); + + if (!key) + return FALSE; + + if (key->value) { + /* strip the leading spaces. We can safely strip \n\r and + * friends too, they should not happen here anyway. */ + while (PROFILE_isspace(*value)) + value++; + + if (!strcmp(key->value, value)) { + if (trace > 1) + htrc(" no change needed\n" ); + + return TRUE; /* No change needed */ + } // endif value + + if (trace > 1) + htrc(" replacing '%s'\n", key->value); + + free(key->value); + } else if (trace > 1) + htrc(" creating key\n" ); + + key->value = malloc(strlen(value) + 1); + strcpy(key->value, value); + CurProfile->changed = TRUE; + } // endelse + + return TRUE; +} // end of PROFILE_SetString + + +/*********************************************************************** + * PROFILE_GetStringItem + * + * Convenience function that turns a string 'xxx, yyy, zzz' into + * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'. + ***********************************************************************/ +char *PROFILE_GetStringItem(char* start) +{ + char *lpchX, *lpch; + + for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++) { + if (*lpchX == ',') { + if (lpch) + *lpch = '\0'; + else + *lpchX = '\0'; + + while(*(++lpchX)) + if (!PROFILE_isspace(*lpchX)) + return lpchX; + + } else if (PROFILE_isspace(*lpchX) && !lpch) { + lpch = lpchX; + } else + lpch = NULL; + + } // endfor lpchX + + if (lpch) + *lpch = '\0'; + + return NULL; +} // end of PROFILE_GetStringItem + +/********************************************************************** + * if allow_section_name_copy is TRUE, allow the copying : + * - of Section names if 'section' is NULL + * - of Keys in a Section if 'entry' is NULL + * (see MSDN doc for GetPrivateProfileString) + **********************************************************************/ +static int PROFILE_GetPrivateProfileString(LPCSTR section, LPCSTR entry, + LPCSTR def_val, LPSTR buffer, + uint len, LPCSTR filename, + BOOL allow_section_name_copy) +{ + int ret; + LPSTR pDefVal = NULL; + + if (!filename) + filename = "win.ini"; + + /* strip any trailing ' ' of def_val. */ + if (def_val) { + LPSTR p = (LPSTR)&def_val[strlen(def_val)]; // even "" works ! + + while (p > def_val) + if ((*(--p)) != ' ') + break; + + if (*p == ' ') { /* ouch, contained trailing ' ' */ + int len = p - (LPSTR)def_val; + + pDefVal = malloc(len + 1); + strncpy(pDefVal, def_val, len); + pDefVal[len] = '\0'; + } // endif *p + + } // endif def_val + + if (!pDefVal) + pDefVal = (LPSTR)def_val; + + EnterCriticalSection(&PROFILE_CritSect); + + if (PROFILE_Open(filename)) { + if ((allow_section_name_copy) && (section == NULL)) + ret = PROFILE_GetSectionNames(buffer, len); + else + /* PROFILE_GetString already handles the 'entry == NULL' case */ + ret = PROFILE_GetString(section, entry, pDefVal, buffer, len); + + } else { + strncpy(buffer, pDefVal, len); + ret = strlen(buffer); + } // endif Open + + LeaveCriticalSection(&PROFILE_CritSect); + + if (pDefVal != def_val) /* allocated */ + memfree(pDefVal); + + return ret; +} // end of PROFILE_GetPrivateProfileString + +/********************** API functions **********************************/ + +/*********************************************************************** + * GetPrivateProfileStringA (KERNEL32.@) + ***********************************************************************/ +int GetPrivateProfileString(LPCSTR section, LPCSTR entry, LPCSTR def_val, + LPSTR buffer, uint len, LPCSTR filename) +{ + return PROFILE_GetPrivateProfileString(section, entry, def_val, + buffer, len, filename, TRUE); +} // end of GetPrivateProfileString + + +/*********************************************************************** + * GetPrivateProfileIntA (KERNEL32.@) + ***********************************************************************/ +uint GetPrivateProfileInt(LPCSTR section, LPCSTR entry, + int def_val, LPCSTR filename) +{ + char buffer[20]; + int result; + + if (!PROFILE_GetPrivateProfileString(section, entry, "", buffer, + sizeof(buffer), filename, FALSE)) + return def_val; + + /* FIXME: if entry can be found but it's empty, then Win16 is + * supposed to return 0 instead of def_val ! Difficult/problematic + * to implement (every other failure also returns zero buffer), + * thus wait until testing framework avail for making sure nothing + * else gets broken that way. */ + if (!buffer[0]) + return (uint)def_val; + + /* Don't use strtol() here ! + * (returns LONG_MAX/MIN on overflow instead of "proper" overflow) + YES, scan for unsigned format ! (otherwise compatibility error) */ + if (!sscanf(buffer, "%u", &result)) + return 0; + + return (uint)result; +} // end of GetPrivateProfileInt + + +/*********************************************************************** + * GetPrivateProfileSectionA (KERNEL32.@) + ***********************************************************************/ +int GetPrivateProfileSection(LPCSTR section, LPSTR buffer, + DWORD len, LPCSTR filename) +{ + int ret = 0; + + EnterCriticalSection( &PROFILE_CritSect ); + + if (PROFILE_Open(filename)) + ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, + FALSE, TRUE); + + LeaveCriticalSection( &PROFILE_CritSect ); + return ret; +} // end of GetPrivateProfileSection + + +/*********************************************************************** + * WritePrivateProfileStringA (KERNEL32.@) + ***********************************************************************/ +BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry, + LPCSTR string, LPCSTR filename) +{ + BOOL ret = FALSE; + + EnterCriticalSection( &PROFILE_CritSect ); + + if (PROFILE_Open(filename)) { + if (!section && !entry && !string) /* documented "file flush" case */ + PROFILE_ReleaseFile(); /* always return FALSE in this case */ + else { + if (!section) { + //FIXME("(NULL?,%s,%s,%s)? \n",entry,string,filename); + } else { + ret = PROFILE_SetString(section, entry, string, FALSE); + + if (ret) + ret = PROFILE_FlushFile(); + + } // endif section + + } // endif section || entry|| string + + } // endif Open + + LeaveCriticalSection( &PROFILE_CritSect ); + return ret; +} // end of WritePrivateProfileString + + +/*********************************************************************** + * WritePrivateProfileSectionA (KERNEL32.@) + ***********************************************************************/ +BOOL WritePrivateProfileSection(LPCSTR section, + LPCSTR string, LPCSTR filename ) +{ + BOOL ret = FALSE; + LPSTR p ; + + EnterCriticalSection(&PROFILE_CritSect); + + if (PROFILE_Open(filename)) { + if (!section && !string) + PROFILE_ReleaseFile(); /* always return FALSE in this case */ + else if (!string) { /* delete the named section*/ + ret = PROFILE_SetString(section, NULL, NULL, FALSE); + + if (ret) + ret = PROFILE_FlushFile(); + } else { + PROFILE_DeleteAllKeys(section); + ret = TRUE; + + while (*string) { + LPSTR buf = malloc(strlen(string) + 1); + strcpy(buf, string); + + if ((p = strchr(buf, '='))) { + *p='\0'; + ret = PROFILE_SetString(section, buf, p+1, TRUE); + } // endif p + + free(buf); + string += strlen(string) + 1; + + if (ret) + ret = PROFILE_FlushFile(); + + } // endwhile *string + + } // endelse + + } // endif Open + + LeaveCriticalSection(&PROFILE_CritSect); + return ret; +} // end of WritePrivateProfileSection + + +/*********************************************************************** + * GetPrivateProfileSectionNamesA (KERNEL32.@) + * + * Returns the section names contained in the specified file. + * FIXME: Where do we find this file when the path is relative? + * The section names are returned as a list of strings with an extra + * '\0' to mark the end of the list. Except for that the behavior + * depends on the Windows version. + * + * Win95: + * - if the buffer is 0 or 1 character long then it is as if it was of + * infinite length. + * - otherwise, if the buffer is to small only the section names that fit + * are returned. + * - note that this means if the buffer was to small to return even just + * the first section name then a single '\0' will be returned. + * - the return value is the number of characters written in the buffer, + * except if the buffer was too smal in which case len-2 is returned + * + * Win2000: + * - if the buffer is 0, 1 or 2 characters long then it is filled with + * '\0' and the return value is 0 + * - otherwise if the buffer is too small then the first section name that + * does not fit is truncated so that the string list can be terminated + * correctly (double '\0') + * - the return value is the number of characters written in the buffer + * except for the trailing '\0'. If the buffer is too small, then the + * return value is len-2 + * - Win2000 has a bug that triggers when the section names and the + * trailing '\0' fit exactly in the buffer. In that case the trailing + * '\0' is missing. + * + * Wine implements the observed Win2000 behavior (except for the bug). + * + * Note that when the buffer is big enough then the return value may be any + * value between 1 and len-1 (or len in Win95), including len-2. + */ +DWORD GetPrivateProfileSectionNames(LPSTR buffer, DWORD size, LPCSTR filename) +{ + DWORD ret = 0; + + if (trace > 1) + htrc("GPPSN: filename=%s\n", filename); + + EnterCriticalSection(&PROFILE_CritSect); + + if (PROFILE_Open(filename)) + ret = PROFILE_GetSectionNames(buffer, size); + + LeaveCriticalSection(&PROFILE_CritSect); + return ret; +} // end of GetPrivateProfileSectionNames + + +/************************************************************************ + * Program to test the above + ************************************************************************/ +#ifdef TEST_MODULE +int main(int argc, char**argv) { + char buff[128]; + char *p, *inifile = "D:\\Plug\\Data\\contact.ini"; + DWORD n; + + n = GetPrivateProfileSectionNames(buff, 128, inifile); + printf("Sections: n=%d\n", n); + + for (p = buff; *p; p += (strlen(p) + 1)) + printf("section=[%s]\n", p); + + GetPrivateProfileString("BER", "name", "?", buff, 128, inifile); + printf("[BER](name) = %s\n", buff); + + WritePrivateProfileString("FOO", "city", NULL, inifile); + GetPrivateProfileString("FOO", "city", "?", buff, 128, inifile); + printf("[FOO](city) = %s\n", buff); + + printf("FOO city: "); + fgets(buff, sizeof(buff), stdin); + if (buff[strlen(buff) - 1] == '\n') + buff[strlen(buff) - 1] = '\0'; + WritePrivateProfileString("FOO", "city", buff, inifile); + GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile); + printf("After write, [FOO](City) = %s\n", buff); + + printf("New city: "); + fgets(buff, sizeof(buff), stdin); + if (buff[strlen(buff) - 1] == '\n') + buff[strlen(buff) - 1] = '\0'; + WritePrivateProfileString("FOO", "city", buff, inifile); + GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile); + printf("After update, [FOO](City) = %s\n", buff); + + printf("FOO name: "); + fgets(buff, sizeof(buff), stdin); + if (buff[strlen(buff) - 1] == '\n') + buff[strlen(buff) - 1] = '\0'; + WritePrivateProfileString("FOO", "name", buff, inifile); + GetPrivateProfileString("FOO", "name", "X", buff, 128, inifile); + printf("[FOO](name) = %s\n", buff); +} // end of main +#endif // TEST_MODULE diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp new file mode 100644 index 00000000000..95b98931680 --- /dev/null +++ b/storage/connect/libdoc.cpp @@ -0,0 +1,788 @@ +/******************************************************************/ +/* Implementation of XML document processing using libxml2 */ +/* Author: Olivier Bertrand 2007-2013 */ +/******************************************************************/ +#include <string.h> +#include <stdio.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +//#if defined(WIN32) +//#include <windows.h> +//#else // !WIN32 +#include "my_global.h" +//#endif // !WIN32 + +#if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED) +#error "XPath not supported" +#endif + +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +#include "libdoc.h" + +#include "sql_string.h" + +extern "C" { +extern char version[]; +extern int trace; +} // "C" + +/******************************************************************/ +/* Return a LIBXMLDOC as a XMLDOC. */ +/******************************************************************/ +PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + return (PXDOC) new(g) LIBXMLDOC(nsl, nsdf, enc, fp); + } // end of GetLibxmlDoc + +/******************************************************************/ +/* XML library initialization function. */ +/******************************************************************/ +void XmlInitParserLib(void) + { + xmlInitParser(); + } // end of XmlInitParserLib + +/******************************************************************/ +/* XML library cleanup function. */ +/******************************************************************/ +void XmlCleanupParserLib(void) + { + xmlCleanupParser(); + xmlMemoryDump(); + } // end of XmlCleanupParserLib + +/******************************************************************/ +/* Close a loaded libxml2 XML file. */ +/******************************************************************/ +void CloseXML2File(PGLOBAL g, PFBLOCK fp, bool all) + { + PX2BLOCK xp = (PX2BLOCK)fp; + + if (xp && xp->Count > 1 && !all) { + xp->Count--; + } else if (xp && xp->Count > 0) { + xmlFreeDoc(xp->Docp); + xp->Count = 0; + } // endif + + } // end of CloseXML2File + +/* ---------------------- class LIBXMLDOC ----------------------- */ + +/******************************************************************/ +/* LIBXMLDOC constructor. */ +/******************************************************************/ +LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) + : XMLDOCUMENT(nsl, nsdf, enc) + { + assert (!fp || fp->Type == TYPE_FB_XML2); + Docp = (fp) ? ((PX2BLOCK)fp)->Docp : NULL; + Nlist = NULL; + Ctxp = NULL; + Xop = NULL; + } // end of LIBXMLDOC constructor + +/******************************************************************/ +/* Initialize XML parser and check library compatibility. */ +/******************************************************************/ +bool LIBXMLDOC::Initialize(PGLOBAL g) + { + int n = xmlKeepBlanksDefault(1); + return MakeNSlist(g); + } // end of Initialize + +/******************************************************************/ +/* Parse the XML file and construct node tree in memory. */ +/******************************************************************/ +bool LIBXMLDOC::ParseFile(char *fn) + { + if ((Docp = xmlParseFile(fn))) { + if (Docp->encoding) + Encoding = (char*)Docp->encoding; + + return false; + } else + return true; + + } // end of ParseFile + +/******************************************************************/ +/* Create or reuse an Xblock for this document. */ +/******************************************************************/ +PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) + { + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + PX2BLOCK xp = (PX2BLOCK)PlugSubAlloc(g, NULL, sizeof(X2BLOCK)); + + memset(xp, 0, sizeof(X2BLOCK)); + xp->Next = (PX2BLOCK)dup->Openlist; + dup->Openlist = (PFBLOCK)xp; + xp->Type = TYPE_FB_XML2; + xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1); + strcpy((char*)xp->Fname, fn); + xp->Count = 1; + xp->Length = (m == MODE_READ) ? 1 : 0; + xp->Retcode = rc; + xp->Docp = Docp; +// xp->Ctxp = Ctxp; +// xp->Xop = Xop; + + // Return xp as a fp + return (PFBLOCK)xp; + } // end of LinkXblock + +/******************************************************************/ +/* Construct and add the XML processing instruction node. */ +/******************************************************************/ +bool LIBXMLDOC::NewDoc(PGLOBAL g, char *ver) + { + return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL); + } // end of NewDoc + +/******************************************************************/ +/* Add a new comment node to the document. */ +/******************************************************************/ +void LIBXMLDOC::AddComment(PGLOBAL g, char *txtp) + { + xmlNodePtr cp = xmlNewDocComment(Docp, BAD_CAST txtp); + xmlAddChild((xmlNodePtr)Docp, cp); + } // end of AddText + +/******************************************************************/ +/* Return the node class of the root of the document. */ +/******************************************************************/ +PXNODE LIBXMLDOC::GetRoot(PGLOBAL g) + { + xmlNodePtr root = xmlDocGetRootElement(Docp); + + if (!root) + return NULL; + + return new(g) XML2NODE(this, root); + } // end of GetRoot + +/******************************************************************/ +/* Create a new root element and return its class node. */ +/******************************************************************/ +PXNODE LIBXMLDOC::NewRoot(PGLOBAL g, char *name) + { + xmlNodePtr root = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); + + if (root) { + xmlDocSetRootElement(Docp, root); + return new(g) XML2NODE(this, root); + } else + return NULL; + + } // end of NewRoot + +/******************************************************************/ +/* Return a void XML2NODE node class. */ +/******************************************************************/ +PXNODE LIBXMLDOC::NewPnode(PGLOBAL g, char *name) + { + xmlNodePtr nop; + + if (name) { + nop = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); + + if (nop == NULL) + return NULL; + + } else + nop = NULL; + + return new(g) XML2NODE(this, nop); + } // end of NewPnode + +/******************************************************************/ +/* Return a void XML2ATTR node class. */ +/******************************************************************/ +PXATTR LIBXMLDOC::NewPattr(PGLOBAL g) + { + return new(g) XML2ATTR(this, NULL, NULL); + } // end of NewPattr + +/******************************************************************/ +/* Return a void XML2ATTR node class. */ +/******************************************************************/ +PXLIST LIBXMLDOC::NewPlist(PGLOBAL g) + { + return new(g) XML2NODELIST(this, NULL); + } // end of NewPlist + +/******************************************************************/ +/* Dump the node tree to a new XML file. */ +/******************************************************************/ +int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn) + { + int rc = 0; + FILE *of; + + if (!(of= global_fopen(g, MSGID_CANNOT_OPEN, ofn, "w"))) + return -1; + +#if 1 + // This function does not crash ( + if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) { + xmlErrorPtr err = xmlGetLastError(); + + strcpy(g->Message, (err) ? err->message : "Error saving XML doc" + ); + rc = -1; + } // endif Save +// rc = xmlDocDump(of, Docp); +#else // 0 + // Until this function is fixed, do the job ourself + xmlNodePtr Rootp; + + // Save the modified document + fprintf(of, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", Encoding); + fprintf(of, "<!-- Created by CONNECT %s -->\n", version); + + if (!(Rootp = xmlDocGetRootElement(Docp))) + return 1; + + Buf = (char*)PlugSubAlloc(g, NULL, 1024); + rc = iconv_close(Cd2); + Cd2 = iconv_open(Encoding, "UTF-8"); + rc = CheckDocument(of, Rootp); +#endif // 0 + + fclose(of); + return rc; + } // end of Dump + +/******************************************************************/ +/* Free the document, cleanup the XML library, and */ +/* debug memory for regression tests. */ +/******************************************************************/ +void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) + { + if (xp && xp->Count == 1) { + if (Xop) + xmlXPathFreeObject(Xop); + + if (Ctxp) + xmlXPathFreeContext(Ctxp); + + } // endif Count + + CloseXML2File(g, xp, false); + } // end of Close + +/******************************************************************/ +/* Evaluate the passed Xpath from the passed context node. */ +/******************************************************************/ +xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp) + { + xmlNodeSetPtr nl; + + if (trace) + htrc("GetNodeList %s np=%p\n", xp, np); + + if (!Ctxp) { + // Init Xpath + xmlXPathInit(); + + // Create xpath evaluation context + if (!(Ctxp = xmlXPathNewContext(Docp))) { + strcpy(g->Message, MSG(XPATH_CNTX_ERR)); + + if (trace) + htrc("Context error: %s\n", g->Message); + + return NULL; + } // endif xpathCtx + + // Register namespaces from list (if any) + for (PNS nsp = Namespaces; nsp; nsp = nsp->Next) + if (xmlXPathRegisterNs(Ctxp, BAD_CAST nsp->Prefix, + BAD_CAST nsp->Uri)) { + sprintf(g->Message, MSG(REGISTER_ERR), nsp->Prefix, nsp->Uri); + + if (trace) + htrc("Ns error: %s\n", g->Message); + + return NULL; + } // endif Registering + + } else + xmlXPathFreeNodeSetList(Xop); // To be checked + + // Set the context to the calling node + Ctxp->node = np; + + // Evaluate table xpath + if (!(Xop = xmlXPathEval(BAD_CAST xp, Ctxp))) { + sprintf(g->Message, MSG(XPATH_EVAL_ERR), xp); + + if (trace) + htrc("Path error: %s\n", g->Message); + + return NULL; + } else + nl = Xop->nodesetval; + + if (trace) + htrc("GetNodeList nl=%p n=%p\n", nl, (nl) ? nl->nodeNr : 0); + + return nl; + } // end of GetNodeList + +#if 0 // Not used anymore +/******************************************************************/ +/* CheckDocument: check if the document is ok to dump. */ +/* Currently this does the dumping of the document. */ +/******************************************************************/ +bool LIBXMLDOC::CheckDocument(FILE *of, xmlNodePtr np) + { + int n; + bool b; + + if (!np) + return true; + + if (np->type == XML_ELEMENT_NODE) { + n = fprintf(of, "<%s", np->name); + b = CheckDocument(of, (xmlNodePtr)np->properties); + + if (np->children) + n = fprintf(of, ">"); + else + n = fprintf(of, "/>"); + + } else if (np->type == XML_ATTRIBUTE_NODE) + n = fprintf(of, " %s=\"", np->name); + else if (np->type == XML_TEXT_NODE) + n = fprintf(of, "%s", Encode(NULL, (char*)np->content)); + else if (np->type == XML_COMMENT_NODE) + n = fprintf(of, "%s", Encode(NULL, (char*)np->content)); + + b = CheckDocument(of, np->children); + + if (np->type == XML_ATTRIBUTE_NODE) + n = fprintf(of, "\""); + else if (!b && np->type == XML_ELEMENT_NODE) + n = fprintf(of, "</%s>", np->name); + + b = CheckDocument(of, np->next); + return false; + } // end of CheckDocument + +/******************************************************************/ +/* Convert node or attribute content to latin characters. */ +/******************************************************************/ +int LIBXMLDOC::Decode(xmlChar *cnt, char *buf, int n) + { + const char *txt = (const char *)cnt; + uint dummy_errors; + uint32 len= copy_and_convert(buf, n, &my_charset_utf8_general_ci, txt, + strlen(txt), &my_charset_utf8_general_ci, + &dummy_errors); + buf[len]= '\0'; + return 0; + } // end of Decode + +/******************************************************************/ +/* Convert node or attribute content to latin characters. */ +/******************************************************************/ +xmlChar *LIBXMLDOC::Encode(PGLOBAL g, char *txt) + { + const CHARSET_INFO *ics= &my_charset_utf8_general_ci; + const CHARSET_INFO *ocs= &my_charset_utf8_general_ci; + size_t i = strlen(txt); + size_t o = i * ocs->mbmaxlen / ics->mbmaxlen + 1; + char *buf; + if (g) { + buf = (char*)PlugSubAlloc(g, NULL, o); + } else { + o = 1024; + buf = Buf; + } // endif g + uint dummy_errors; + uint32 len= copy_and_convert(buf, o, ocs, + txt, i, ics, + &dummy_errors); + buf[len]= '\0'; + return BAD_CAST buf; + } // end of Encode +#endif // 0 + +/* ---------------------- class XML2NODE ------------------------ */ + +/******************************************************************/ +/* XML2NODE constructor. */ +/******************************************************************/ +XML2NODE::XML2NODE(PXDOC dp, xmlNodePtr np) : XMLNODE(dp) + { + Docp = ((PXDOC2)dp)->Docp; + Content = NULL; + Nodep = np; + } // end of XML2NODE constructor + +int XML2NODE::GetType(void) + { + if (trace) + htrc("GetType type=%d\n", Nodep->type); + + return Nodep->type; + } // end of GetType + +/******************************************************************/ +/* Return the node class of next sibling of the node. */ +/******************************************************************/ +PXNODE XML2NODE::GetNext(PGLOBAL g) + { + if (!Nodep->next) + Next = NULL; + else if (!Next) + Next = new(g) XML2NODE(Doc, Nodep->next); + + return Next; + } // end of GetNext + +/******************************************************************/ +/* Return the node class of first children of the node. */ +/******************************************************************/ +PXNODE XML2NODE::GetChild(PGLOBAL g) + { + if (!Nodep->children) + Children = NULL; + else if (!Children) + Children = new(g) XML2NODE(Doc, Nodep->children); + + return Children; + } // end of GetChild + +/******************************************************************/ +/* Return the content of a node and subnodes. */ +/******************************************************************/ +RCODE XML2NODE::GetContent(PGLOBAL g, char *buf, int len) + { + RCODE rc = RC_OK; + + if (Content) + xmlFree(Content); + + if ((Content = xmlNodeGetContent(Nodep))) { + char *extra = " \t\r\n"; + char *p1 = (char*)Content, *p2 = buf; + bool b = false; + + // Copy content eliminating extra characters + for (; *p1; p1++) + if ((p2 - buf) < len) { + if (strchr(extra, *p1)) { + if (b) { + // This to have one blank between sub-nodes + *p2++ = ' '; + b = false; + } // endif b + + } else { + *p2++ = *p1; + b = true; + } // endif p1 + + } else { + sprintf(g->Message, "Truncated %s content", Nodep->name); + rc = RC_INFO; + } // endif len + + *p2 = 0; + + if (trace) + htrc("GetText buf='%s' len=%d\n", buf, len); + + xmlFree(Content); + Content = NULL; + } else + *buf = '\0'; + + return rc; + } // end of GetText + +/******************************************************************/ +/* Set the content of a node. */ +/******************************************************************/ +bool XML2NODE::SetContent(PGLOBAL g, char *txtp, int len) + { + xmlChar *buf = xmlEncodeEntitiesReentrant(Docp, BAD_CAST txtp); + + if (trace) + htrc("SetContent %s -> %s\n", txtp, buf); + + xmlNodeSetContent(Nodep, buf); + xmlFree(buf); + return false; + } // end of SetContent + +/******************************************************************/ +/* Return a clone of this node. */ +/******************************************************************/ +PXNODE XML2NODE::Clone(PGLOBAL g, PXNODE np) + { + if (np) { + ((PNODE2)np)->Nodep = Nodep; + return np; + } else + return new(g) XML2NODE(Doc, Nodep); + + } // end of Clone + +/******************************************************************/ +/* Return the list of all or matching children that are elements.*/ +/******************************************************************/ +PXLIST XML2NODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp) + { + if (trace) + htrc("GetChildElements %s\n", xp); + + return SelectNodes(g, (xp) ? xp : (char*)"*", lp); + } // end of GetChildElements + +/******************************************************************/ +/* Return the list of nodes verifying the passed Xpath. */ +/******************************************************************/ +PXLIST XML2NODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) + { + if (trace) + htrc("SelectNodes %s\n", xp); + + xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); + + if (lp) { + ((PLIST2)lp)->Listp = nl; + return lp; + } else + return new(g) XML2NODELIST(Doc, nl); + + } // end of SelectNodes + +/******************************************************************/ +/* Return the first node verifying the passed Xapth. */ +/******************************************************************/ +PXNODE XML2NODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) + { + if (trace) + htrc("SelectSingleNode %s\n", xp); + + xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); + + if (nl && nl->nodeNr) { + if (np) { + ((PNODE2)np)->Nodep = nl->nodeTab[0]; + return np; + } else + return new(g) XML2NODE(Doc, nl->nodeTab[0]); + + } else + return NULL; + + } // end of SelectSingleNode + +/******************************************************************/ +/* Return the node attribute with the specified name. */ +/******************************************************************/ +PXATTR XML2NODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap) + { + if (trace) + htrc("GetAttribute %s\n", name); + + xmlAttrPtr atp = xmlHasProp(Nodep, BAD_CAST name); + + if (atp) { + if (ap) { + ((PATTR2)ap)->Atrp = atp; + ((PATTR2)ap)->Parent = Nodep; + return ap; + } else + return new(g) XML2ATTR(Doc, atp, Nodep); + + } else + return NULL; + + } // end of GetAttribute + +/******************************************************************/ +/* Add a new child node to this node and return it. */ +/******************************************************************/ +PXNODE XML2NODE::AddChildNode(PGLOBAL g, char *name, PXNODE np) + { + char *p, *pn, *pf = NULL; + + if (trace) + htrc("AddChildNode %s\n", name); + + // Is a prefix specified + if ((pn = strchr(name, ':'))) { + pf = name; + *pn++ = '\0'; // Separate name from prefix + } else + pn = name; + + // If name has the format m[n] only m is taken as node name + if ((p = strchr(pn, '['))) + p = BufAlloc(g, pn, p - pn); + else + p = pn; + + xmlNodePtr nop = xmlNewChild(Nodep, NULL, BAD_CAST p, NULL); + + if (!nop) + return NULL; + + if (pf) { + // Prefixed name, is it the default NS prefix? + if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) + pf = NULL; // Default namespace + + xmlNsPtr nsp = xmlSearchNs(Docp, nop, BAD_CAST pf); + + if (!nsp) + nsp = xmlNewNs(nop, NULL, BAD_CAST pf); + + // Set node namespace + nop->ns = nsp; + *(--p) = ':'; // Restore Xname + } else if (Doc->DefNs && xmlSearchNs(Docp, nop, NULL)) + // Not in default namespace + nop->ns = xmlNewNs(nop, BAD_CAST "", NULL); + + if (np) + ((PNODE2)np)->Nodep = nop; + else + np = new(g) XML2NODE(Doc, nop); + + return NewChild(np); + } // end of AddChildNode + +/******************************************************************/ +/* Add a new property to this node and return it. */ +/******************************************************************/ +PXATTR XML2NODE::AddProperty(PGLOBAL g, char *name, PXATTR ap) + { + if (trace) + htrc("AddProperty %s\n", name); + + xmlAttrPtr atp = xmlNewProp(Nodep, BAD_CAST name, NULL); + + if (atp) { + if (ap) { + ((PATTR2)ap)->Atrp = atp; + ((PATTR2)ap)->Parent = Nodep; + return ap; + } else + return new(g) XML2ATTR(Doc, atp, Nodep); + + } else + return NULL; + + } // end of AddProperty + +/******************************************************************/ +/* Add a new text node to this node. */ +/******************************************************************/ +void XML2NODE::AddText(PGLOBAL g, char *txtp) + { + if (trace) + htrc("AddText %s\n", txtp); + + // This is to avoid a blank line when inserting a new line + xmlNodePtr np = xmlGetLastChild(Nodep); + + if (np && np->type == XML_TEXT_NODE) { + xmlUnlinkNode(np); + xmlFreeNode(np); + } // endif type + + // Add the new text + xmlAddChild(Nodep, xmlNewText(BAD_CAST txtp)); + } // end of AddText + +/******************************************************************/ +/* Remove a child node from this node. */ +/******************************************************************/ +void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp) + { + xmlNodePtr np = ((PNODE2)dnp)->Nodep; + xmlNodePtr text = np->next; + + // This is specific to row nodes + if (text && text->type == XML_TEXT_NODE) { + xmlUnlinkNode(text); + xmlFreeNode(text); + } // endif type + + xmlUnlinkNode(np); + xmlFreeNode(np); + Delete(dnp); + } // end of DeleteChild + +/* -------------------- class XML2NODELIST ---------------------- */ + +/******************************************************************/ +/* XML2NODELIST constructor. */ +/******************************************************************/ +XML2NODELIST::XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp) + : XMLNODELIST(dp) + { + Listp = lp; + } // end of XML2NODELIST constructor + +/******************************************************************/ +/* Return the length of the list. */ +/******************************************************************/ +int XML2NODELIST::GetLength(void) + { + return (Listp) ? Listp->nodeNr : 0; + } // end of GetLength + +/******************************************************************/ +/* Return the nth element of the list. */ +/******************************************************************/ +PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np) + { + if (trace) + htrc("GetItem %d\n", n); + + if (!Listp || Listp->nodeNr <= n) + return NULL; + + if (np) { + ((PNODE2)np)->Nodep = Listp->nodeTab[n]; + return np; + } else + return new(g) XML2NODE(Doc, Listp->nodeTab[n]); + + } // end of GetItem + +/* ---------------------- class XML2ATTR ------------------------ */ + +/******************************************************************/ +/* XML2ATTR constructor. */ +/******************************************************************/ +XML2ATTR::XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np) + : XMLATTRIBUTE(dp) + { + Atrp = ap; + Parent = np; + } // end of XML2ATTR constructor + +/******************************************************************/ +/* Set the content of an attribute. */ +/******************************************************************/ +bool XML2ATTR::SetText(PGLOBAL g, char *txtp, int len) + { + if (trace) + htrc("SetText %s %d\n", txtp, len); + + xmlSetProp(Parent, Atrp->name, BAD_CAST txtp); + return false; + } // end of SetText diff --git a/storage/connect/libdoc.h b/storage/connect/libdoc.h new file mode 100644 index 00000000000..97f97f99ccc --- /dev/null +++ b/storage/connect/libdoc.h @@ -0,0 +1,144 @@ +/******************************************************************/ +/* Declaration of XML document processing using libxml2 */ +/* Author: Olivier Bertrand 2007-2012 */ +/******************************************************************/ +#include "plgxml.h" + +typedef class LIBXMLDOC *PXDOC2; +typedef class XML2NODE *PNODE2; +typedef class XML2ATTR *PATTR2; +typedef class XML2NODELIST *PLIST2; + +/******************************************************************/ +/* XML2 block. Must have the same layout than FBLOCK up to Type. */ +/******************************************************************/ +typedef struct _x2block { /* Loaded XML file block */ + struct _x2block *Next; + LPCSTR Fname; /* Point on file name */ + size_t Length; /* Used to tell if read mode */ + short Count; /* Nb of times file is used */ + short Type; /* TYPE_FB_XML */ + int Retcode; /* Return code from Load */ + xmlDocPtr Docp; /* Document interface pointer */ +// xmlXPathContextPtr Ctxp; +// xmlXPathObjectPtr Xop; + } X2BLOCK, *PX2BLOCK; + +/******************************************************************/ +/* Declaration of libxml2 document. */ +/******************************************************************/ +class LIBXMLDOC : public XMLDOCUMENT { + friend class XML2NODE; + friend class XML2ATTR; + public: + // Constructor + LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp); + + // Properties + virtual short GetDocType(void) {return TYPE_FB_XML2;} + virtual void *GetDocPtr(void) {return Docp;} + + // Methods + virtual bool Initialize(PGLOBAL g); + virtual bool ParseFile(char *fn); + virtual bool NewDoc(PGLOBAL g, char *ver); + virtual void AddComment(PGLOBAL g, char *com); + virtual PXNODE GetRoot(PGLOBAL g); + virtual PXNODE NewRoot(PGLOBAL g, char *name); + virtual PXNODE NewPnode(PGLOBAL g, char *name); + virtual PXATTR NewPattr(PGLOBAL g); + virtual PXLIST NewPlist(PGLOBAL g); + virtual int DumpDoc(PGLOBAL g, char *ofn); + virtual void CloseDoc(PGLOBAL g, PFBLOCK xp); + virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn); + + protected: +// bool CheckDocument(FILE *of, xmlNodePtr np); + xmlNodeSetPtr GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp); + int Decode(xmlChar *cnt, char *buf, int n); + xmlChar *Encode(PGLOBAL g, char *txt); + + // Members + xmlDocPtr Docp; + xmlNodeSetPtr Nlist; + xmlXPathContextPtr Ctxp; + xmlXPathObjectPtr Xop; + char *Buf; // Temporary +}; // end of class LIBXMLDOC + +/******************************************************************/ +/* Declaration of libxml2 node. */ +/******************************************************************/ +class XML2NODE : public XMLNODE { + friend class LIBXMLDOC; + friend class XML2NODELIST; + public: + // Properties + virtual char *GetName(PGLOBAL g) {return (char*)Nodep->name;} + virtual int GetType(void); + virtual PXNODE GetNext(PGLOBAL g); + virtual PXNODE GetChild(PGLOBAL g); + + // Methods + virtual RCODE GetContent(PGLOBAL g, char *buf, int len); + virtual bool SetContent(PGLOBAL g, char *txtp, int len); + virtual PXNODE Clone(PGLOBAL g, PXNODE np); + virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp); + virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp); + virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np); + virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap); + virtual PXNODE AddChildNode(PGLOBAL g, char *name, PXNODE np); + virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap); + virtual void AddText(PGLOBAL g, char *txtp); + virtual void DeleteChild(PGLOBAL g, PXNODE dnp); + + protected: + // Constructor + XML2NODE(PXDOC dp, xmlNodePtr np); + + // Members + xmlDocPtr Docp; + xmlChar *Content; + xmlNodePtr Nodep; +}; // end of class XML2NODE + +/******************************************************************/ +/* Declaration of libxml2 node list. */ +/******************************************************************/ +class XML2NODELIST : public XMLNODELIST { + friend class LIBXMLDOC; + friend class XML2NODE; + public: + // Methods + virtual int GetLength(void); + virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); + + protected: + // Constructor + XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp); + + // Members + xmlNodeSetPtr Listp; +}; // end of class XML2NODELIST + +/******************************************************************/ +/* Declaration of libxml2 attribute. */ +/******************************************************************/ +class XML2ATTR : public XMLATTRIBUTE { + friend class LIBXMLDOC; + friend class XML2NODE; + public: + // Properties +//virtual char *GetText(void); + + // Methods + virtual bool SetText(PGLOBAL g, char *txtp, int len); + + protected: + // Constructor + XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np); + + // Members + xmlAttrPtr Atrp; + xmlNodePtr Parent; +}; // end of class XML2ATTR diff --git a/storage/connect/macutil.cpp b/storage/connect/macutil.cpp new file mode 100644 index 00000000000..44382cdafb4 --- /dev/null +++ b/storage/connect/macutil.cpp @@ -0,0 +1,318 @@ +/***********************************************************************/ +/* MACUTIL: Author Olivier Bertrand -- 2008-2012 */ +/* From the article and sample code by Khalid Shaikh. */ +/***********************************************************************/ +#if defined(WIN32) +#include "my_global.h" +#else // !WIN32 +#error This is WIN32 only DLL +#endif // !WIN32 +#include "global.h" +#include "plgdbsem.h" +#include "macutil.h" + +#if 0 // This is placed here just to know what are the actual values +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 +#define MAX_ADAPTER_NAME_LENGTH 256 +#define MAX_ADAPTER_ADDRESS_LENGTH 8 +#define DEFAULT_MINIMUM_ENTITIES 32 +#define MAX_HOSTNAME_LEN 128 +#define MAX_DOMAIN_NAME_LEN 128 +#define MAX_SCOPE_ID_LEN 256 + +#define BROADCAST_NODETYPE 1 +#define PEER_TO_PEER_NODETYPE 2 +#define MIXED_NODETYPE 4 +#define HYBRID_NODETYPE 8 + +#define IP_ADAPTER_DDNS_ENABLED 0x01 +#define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02 +#define IP_ADAPTER_DHCP_ENABLED 0x04 +#define IP_ADAPTER_RECEIVE_ONLY 0x08 +#define IP_ADAPTER_NO_MULTICAST 0x10 +#define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20 +#endif // 0 + +/***********************************************************************/ +/* Implementation of the MACINFO class. */ +/***********************************************************************/ +MACINFO::MACINFO(bool adap, bool fix) + { + Fip = NULL; + Piaf = NULL; + Curp = NULL; + Buflen = 0; + Fix = fix; + Adap = adap; + N = -1; + } // end of MACINFO constructor + +/***********************************************************************/ +/* MACINFO: Return an error message. */ +/***********************************************************************/ +void MACINFO::MakeErrorMsg(PGLOBAL g, DWORD drc) + { + if (drc == ERROR_BUFFER_OVERFLOW) + sprintf(g->Message, + "GetAdaptersInfo: Buffer Overflow buflen=%d nbofadap=%d", + Buflen, N); + else if (drc == ERROR_INVALID_PARAMETER) + strcpy(g->Message, "GetAdaptersInfo: Invalid parameters"); + else if (drc == ERROR_NO_DATA) + strcpy(g->Message, + "No adapter information exists for the local computer"); + else if (drc == ERROR_NOT_SUPPORTED) + strcpy(g->Message, "GetAdaptersInfo is not supported"); + else + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + 0, g->Message, sizeof(g->Message), NULL); + + } // end of MakeErrorMsg + +/***********************************************************************/ +/* MAC: Get the number of found adapters. */ +/***********************************************************************/ +int MACINFO::GetNadap(PGLOBAL g) + { + if (N < 0) { + // Best method + if (Adap) { + DWORD drc = GetAdaptersInfo(NULL, &Buflen); + + if (drc == ERROR_SUCCESS) + N = (Fix) ? 1 : 0; + else if (drc == ERROR_BUFFER_OVERFLOW) + N = Buflen / sizeof(IP_ADAPTER_INFO); + else + MakeErrorMsg(g, drc); + + } else + N = (Fix) ? 1 : 0; + +#if 0 + // This method returns too many adapters + DWORD dw, drc = GetNumberOfInterfaces((PDWORD)&dw); + + if (drc == NO_ERROR) { + N = (int)dw; + Buflen = N * sizeof(IP_ADAPTER_INFO); + } else + MakeErrorMsg(g, 0); +#endif + } // endif MaxSize + + return N; + } // end of GetMaxSize + +/***********************************************************************/ +/* GetMacInfo: Get info for all found adapters. */ +/***********************************************************************/ +bool MACINFO::GetMacInfo(PGLOBAL g) + { + DWORD drc; + + if (GetNadap(g) < 0) + return true; + else if (N == 0) + return false; + + Piaf = (PIP_ADAPTER_INFO)PlugSubAlloc(g, NULL, Buflen); + drc = GetAdaptersInfo(Piaf, &Buflen); + + if (drc == ERROR_SUCCESS) { + Curp = Piaf; // Curp is the first one + return false; // Success + } // endif drc + + MakeErrorMsg(g, drc); + return true; + } // end of GetMacInfo + +/***********************************************************************/ +/* GetMacInfo: Get info for all found adapters. */ +/***********************************************************************/ +bool MACINFO::GetFixedInfo(PGLOBAL g) + { + ULONG len = (uint)sizeof(FIXED_INFO); + DWORD drc; + + Fip = (FIXED_INFO*)PlugSubAlloc(g, NULL, len); + drc = GetNetworkParams(Fip, &len); + + if (drc == ERROR_BUFFER_OVERFLOW) { + Fip = (FIXED_INFO*)PlugSubAlloc(g, NULL, len); + drc = GetNetworkParams(Fip, &len); + } // endif drc + + if (drc != ERROR_SUCCESS) { + sprintf(g->Message, "GetNetworkParams failed. Rc=%08x\n", drc); + return true; + } // endif drc + + return false; + } // end of GetFip + +#if 0 +#define IF_OTHER_ADAPTERTYPE 0 +#define IF_ETHERNET_ADAPTERTYPE 1 +#define IF_TOKEN_RING_ADAPTERTYPE 2 +#define IF_FDDI_ADAPTERTYPE 3 +#define IF_PPP_ADAPTERTYPE 4 +#define IF_LOOPBACK_ADAPTERTYPE 5 +#endif // 0 + +/***********************************************************************/ +/* Get next MAC info. */ +/***********************************************************************/ +bool MACINFO::NextMac(void) + { + if (Curp) + Curp = Curp->Next; + + return Curp != NULL; + } // end of NextMac + +/***********************************************************************/ +/* Get the next MAC address elements. */ +/***********************************************************************/ +bool MACINFO::GetOneInfo(PGLOBAL g, int flag, void *v, int lv) + { + char *p = NULL, buf[260] = ""; + unsigned int i; + int n = 0; + + if (!Curp && flag >= 10) { + // Fix info row, no adapter info available + switch (flag) { + case 13: + case 14: + case 19: + case 22: + case 23: + break; + default: + p = ""; + } // endswitch flag + + } else switch (flag) { + // FIXED INFO + case 1: // Host Name + p = Fip->HostName; + break; + case 2: // Domain Name + p = Fip->DomainName; + break; + case 3: // DNS IPaddress + p = (Fip->CurrentDnsServer) + ? (char*)&Fip->CurrentDnsServer->IpAddress + : (char*)&Fip->DnsServerList.IpAddress; + break; + case 4: // Node Type + n = (int)Fip->NodeType; + break; + case 5: // Scope ID ??? + p = Fip->ScopeId; + break; + case 6: // Routing enabled + n = (int)Fip->EnableRouting; + break; + case 7: // Proxy enabled + n = (int)Fip->EnableProxy; + break; + case 8: // DNS enabled + n = (int)Fip->EnableDns; + break; + // ADAPTERS INFO + case 10: // Name + p = Curp->AdapterName; + break; + case 11: // Description + if ((p = strstr(Curp->Description, " - Packet Scheduler Miniport"))) { + strncpy(buf, Curp->Description, p - Curp->Description); + i = p - Curp->Description; + strncpy(buf, Curp->Description, i); + buf[i] = 0; + p = buf; + } else if ((p = strstr(Curp->Description, + " - Miniport d'ordonnancement de paquets"))) { + i = p - Curp->Description; + strncpy(buf, Curp->Description, i); + buf[i] = 0; + p = buf; + } else + p = Curp->Description; + + break; + case 12: // MAC Address + for (p = buf, i = 0; i < Curp->AddressLength; i++) { + if (i) + strcat(p++, "-"); + + p += sprintf(p, "%.2X", Curp->Address[i]); + } // endfor i + + p = buf; + break; + case 13: // Type +#if 0 // This is not found in the SDK + switch (Curp->Type) { + case IF_ETHERNET_ADAPTERTYPE: p = "Ethernet Adapter"; break; + case IF_TOKEN_RING_ADAPTERTYPE: p = "Token Ring Adapter"; break; + case IF_FDDI_ADAPTERTYPE: p = "FDDI Adapter"; break; + case IF_PPP_ADAPTERTYPE: p = "PPP Adapter"; break; + case IF_LOOPBACK_ADAPTERTYPE: p = "Loop Back Adapter"; break; +// case IF_SLIP_ADAPTERTYPE: p = "Generic Slip Adapter"; break; + default: + sprintf(buf, "Other Adapter, type=%d", Curp->Type); + p = buf; + } // endswitch Type +#endif // 0 + n = (int)Curp->Type; + break; + case 14: // DHCP enabled + n = (int)Curp->DhcpEnabled; + break; + case 15: // IP Address + p = (Curp->CurrentIpAddress) + ? (char*)&Curp->CurrentIpAddress->IpAddress + : (char*)&Curp->IpAddressList.IpAddress; + break; + case 16: // Subnet Mask + p = (Curp->CurrentIpAddress) + ? (char*)&Curp->CurrentIpAddress->IpMask + : (char*)&Curp->IpAddressList.IpMask; + break; + case 17: // Gateway + p = (char*)&Curp->GatewayList.IpAddress; + break; + case 18: // DHCP Server + p = (char*)&Curp->DhcpServer.IpAddress; + break; + case 19: // Have WINS + n = (Curp->HaveWins) ? 1 : 0; + break; + case 20: // Primary WINS + p = (char*)&Curp->PrimaryWinsServer.IpAddress; + break; + case 21: // Secondary WINS + p = (char*)&Curp->SecondaryWinsServer.IpAddress; + break; + case 22: // Lease obtained + n = (int)Curp->LeaseObtained; + break; + case 23: // Lease expires + n = (int)Curp->LeaseExpires; + break; + default: + sprintf(g->Message, "Invalid flag value %d", flag); + return true; + } // endswitch flag + + if (p) + strncpy((char*)v, p, lv); + else + *((int*)v) = n; + + return false; + } // end of ReadColumn diff --git a/storage/connect/macutil.h b/storage/connect/macutil.h new file mode 100644 index 00000000000..8a3e97e12e1 --- /dev/null +++ b/storage/connect/macutil.h @@ -0,0 +1,36 @@ +// MACUTIL.H Olivier Bertrand 2008-2012 +// Get Mac Addresses via GetAdaptersInfo +#if defined(WIN32) +#include <iphlpapi.h> +#else // !WIN32 +#error This is WIN32 only +#endif // !WIN32 +#include "block.h" + +typedef class MACINFO *MACIP; + +/***********************************************************************/ +/* This is the class declaration for MACINFO. */ +/***********************************************************************/ +class DllExport MACINFO : public BLOCK { + public: + // Constructor + MACINFO(bool adap, bool fix); + + // Implementation + int GetNadap(PGLOBAL g); + bool GetMacInfo(PGLOBAL g); + bool GetFixedInfo(PGLOBAL g); + void MakeErrorMsg(PGLOBAL g, DWORD drc); + bool NextMac(void); + bool GetOneInfo(PGLOBAL g, int flag, void *v, int lv); + + // Members + FIXED_INFO *Fip; // Points to fixed info structure + PIP_ADAPTER_INFO Piaf; // Points on Adapter info array + PIP_ADAPTER_INFO Curp; // Points on current Adapt info + ULONG Buflen; // Buffer length + bool Fix; // true if FixedInfo is needed + bool Adap; // true if Piaf is needed + int N; // Number of adapters + }; // end of class MACINFO diff --git a/storage/connect/maputil.cpp b/storage/connect/maputil.cpp new file mode 100644 index 00000000000..b7032f10d2b --- /dev/null +++ b/storage/connect/maputil.cpp @@ -0,0 +1,189 @@ +#include "my_global.h" +#ifdef UNIX +#include "osutil.h" +#include <errno.h> +#include <stddef.h> +#else /* WINDOWS */ +//#include <windows.h> +#include "osutil.h" +#endif /* WINDOWS */ +#include <stdlib.h> +#include <stdio.h> + +#include "global.h" +#include "plgdbsem.h" +#include "maputil.h" + +#ifdef WIN32 +/***********************************************************************/ +/* In Insert mode, just open the file for append. Otherwise */ +/* create the mapping file object. The map handle can be released */ +/* immediately because they will not be used anymore. */ +/* If del is true in DELETE mode, then delete the whole file. */ +/* Returns the file handle that can be used by caller. */ +/***********************************************************************/ +HANDLE CreateFileMap(PGLOBAL g, LPCSTR filename, + MEMMAP *mm, MODE mode, bool del) + { + HANDLE hFile; + HANDLE hFileMap; + DWORD access, share, disposition; + + memset(mm, 0, sizeof(MEMMAP)); + *g->Message = '\0'; + + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + disposition = OPEN_EXISTING; + break; + case MODE_UPDATE: + case MODE_DELETE: + access = GENERIC_READ | GENERIC_WRITE; + share = 0; + disposition = (del) ? TRUNCATE_EXISTING : OPEN_EXISTING; + break; + case MODE_INSERT: + access = GENERIC_WRITE; + share = 0; + disposition = OPEN_ALWAYS; + break; + default: + sprintf(g->Message, MSG(BAD_FUNC_MODE), "CreateFileMap", mode); + return INVALID_HANDLE_VALUE; + } // endswitch + + hFile = CreateFile(filename, access, share, NULL, disposition, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) + if (mode != MODE_INSERT) { + /*****************************************************************/ + /* Create the file-mapping object. */ + /*****************************************************************/ + access = (mode == MODE_READ) ? PAGE_READONLY : PAGE_READWRITE; + hFileMap = CreateFileMapping(hFile, NULL, access, 0, 0, NULL); + + if (!hFileMap) { + DWORD ler = GetLastError(); + + if (ler && ler != 1006) { + sprintf(g->Message, MSG(FILE_MAP_ERROR), filename, ler); + CloseHandle(hFile); + return INVALID_HANDLE_VALUE; + } else { + sprintf(g->Message, MSG(FILE_IS_EMPTY), filename); + return hFile; + } // endif ler + + } // endif hFileMap + + access = (mode == MODE_READ) ? FILE_MAP_READ : FILE_MAP_WRITE; + mm->memory = MapViewOfFile(hFileMap, access, 0, 0, 0); + // lenH is the high-order word of the file size + mm->lenL = GetFileSize(hFile, &mm->lenH); + CloseHandle(hFileMap); // Not used anymore + } else // MODE_INSERT + /*****************************************************************/ + /* The starting point must be the end of file as for append. */ + /*****************************************************************/ + SetFilePointer(hFile, 0, NULL, FILE_END); + + return hFile; + } // end of CreateFileMap + +bool CloseMemMap(LPVOID memory, size_t dwSize) + { + return (memory) ? !UnmapViewOfFile(memory) : false; + } // end of CloseMemMap + +#else /* UNIX */ +// Code to handle Linux and Solaris +#include <sys/types.h> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> + +/***********************************************************************/ +/* In Insert mode, just open the file for append. Otherwise */ +/* create the mapping file object. The map handle can be released */ +/* immediately because they will not be used anymore. */ +/* If del is true in DELETE mode, then delete the whole file. */ +/* Returns the file handle that can be used by caller. */ +/***********************************************************************/ +HANDLE CreateFileMap(PGLOBAL g, LPCSTR fileName, + MEMMAP *mm, MODE mode, bool del) + { + unsigned int openMode; + int protmode; + HANDLE fd; + size_t filesize; + struct stat st; + + memset(mm, 0, sizeof(MEMMAP)); + *g->Message = '\0'; + + switch (mode) { + case MODE_READ: + openMode = O_RDONLY; + protmode = PROT_READ; + break; + case MODE_UPDATE: + case MODE_DELETE: + openMode = (del) ? (O_RDWR | O_TRUNC) : O_RDWR; + protmode = PROT_READ | PROT_WRITE; + break; + case MODE_INSERT: + openMode = (O_WRONLY | O_CREAT | O_APPEND); + protmode = PROT_WRITE; + break; + default: + sprintf(g->Message, MSG(BAD_FUNC_MODE), "CreateFileMap", mode); + return INVALID_HANDLE_VALUE; + } // endswitch + + // Try to open the addressed file. + fd= global_open(g, MSGID_NONE, fileName, openMode); + + if (fd != INVALID_HANDLE_VALUE && mode != MODE_INSERT) { + /* We must know about the size of the file. */ + if (fstat(fd, &st)) { + sprintf(g->Message, MSG(FILE_MAP_ERROR), fileName, errno); + close(fd); + return INVALID_HANDLE_VALUE; + } // endif fstat + + filesize = st.st_size; + + // Now we are ready to load the file. If mmap() is available we try + // this first. If not available or it failed we try to load it. + mm->memory = mmap(NULL, filesize, protmode, MAP_SHARED, fd, 0); + + if (mm->memory != MAP_FAILED) { + mm->lenL = (mm->memory != 0) ? filesize : 0; + mm->lenH = 0; + } else { + strcpy(g->Message, "Memory mapping failed"); + return INVALID_HANDLE_VALUE; + } // endif memory + + } /* endif fd */ + + // mmap() call was successful. ?????????? + return fd; + } // end of CreateFileMap + +bool CloseMemMap(void *memory, size_t dwSize) + { + if (memory) { + // All this must be redesigned + int rc = msync(memory, dwSize, MS_SYNC); + return (munmap(memory, dwSize) < 0) ? true : false; + } else + return false; + + } // end of CloseMemMap + +#endif // UNIX diff --git a/storage/connect/maputil.h b/storage/connect/maputil.h new file mode 100644 index 00000000000..8ab41487a63 --- /dev/null +++ b/storage/connect/maputil.h @@ -0,0 +1,22 @@ +#ifndef __MAPUTIL_H__ +#define __MAPUTIL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *memory; + DWORD lenL; + DWORD lenH; + } MEMMAP; + +HANDLE CreateFileMap(PGLOBAL, LPCSTR, MEMMAP *, MODE, bool); +bool CloseMemMap(void *memory, size_t dwSize); +my_bool CloseFileHandle(HANDLE h); + +#ifdef __cplusplus +} +#endif + +#endif /* __MAPUTIL_H__ */ diff --git a/storage/connect/messages.h b/storage/connect/messages.h new file mode 100644 index 00000000000..b55ec39b235 --- /dev/null +++ b/storage/connect/messages.h @@ -0,0 +1,13 @@ +/**************************************************************************/ +/* NLS messsages definition. */ +/**************************************************************************/ +#if defined(FRENCH) +#if defined(CPX) +#include "frmsg1.h" +#else /* not CPX */ +#include "frmsg2.h" +#endif /* CPX */ +#else /* not FRENCH */ +#include "engmsg.h" +#endif /* FRENCH */ +/* ---------------------------------------------------------------------- */ diff --git a/storage/connect/msgid.h b/storage/connect/msgid.h new file mode 100644 index 00000000000..7a9f9438472 --- /dev/null +++ b/storage/connect/msgid.h @@ -0,0 +1,1013 @@ +#define MSG_BAD_ARRAY_VAL 239 +#define MSG_BAD_SET_CASE 240 +#define MSG_MISSING_ARG 241 +#define MSG_NO_SUB_VAL 242 +#define MSG_PREC_VBLP_NULL 243 +#define MSG_X_ON_TAB 244 +#define MSG_BAD_COLCRT_ARG 245 +#define MSG_BAD_COLSIZE 246 +#define MSG_BAD_COL_ENTRY 247 +#define MSG_BAD_COL_TYPE 248 +#define MSG_BAD_FILE_LIST 249 +#define MSG_BAD_GETVIEW_RET 250 +#define MSG_BAD_OFFSET_VAL 251 +#define MSG_BAD_TABLE_LINE 252 +#define MSG_BAD_TABLE_TYPE 253 +#define MSG_COLSEC_TOO_BIG 254 +#define MSG_COL_HAS_NO_DEF 255 +#define MSG_COL_NOT_CODED 256 +#define MSG_ERASED 257 +#define MSG_ERASE_FAILED 258 +#define MSG_INDEX_DROPPED 259 +#define MSG_INDX_ALL_DROP 260 +#define MSG_INV_QUALIFIER 261 +#define MSG_LOADING_DB 262 +#define MSG_NO_COL_FOUND 263 +#define MSG_NO_COL_SECTION 264 +#define MSG_NO_FEAT_SUPPORT 265 +#define MSG_NO_FILE_LIST 266 +#define MSG_NO_INDEX 267 +#define MSG_NO_MORE_VAR 268 +#define MSG_NO_MUL_VCT 269 +#define MSG_NO_OPT_COLUMN 270 +#define MSG_NO_SUCH_INDEX 271 +#define MSG_NO_SUCH_TABLE 272 +#define MSG_NO_TABLE_INDEX 273 +#define MSG_OPEN_ERROR_ON 274 +#define MSG_RECORD_ERROR 275 +#define MSG_SUBSET_ERROR 276 +#define MSG_TABLE_ALREADY 277 +#define MSG_TABLE_NOT_IN_DB 278 +#define MSG_TABLE_NO_OPT 279 +#define MSG_TAB_NOT_LOADED 280 +#define MSG_TAB_NOT_SPEC 281 +#define MSG_TB_VW_NOTIN_DB 282 +#define MSG_TOO_MANY_COLTAB 283 +#define MSG_TOO_MANY_JUMPS 284 +#define MSG_TOO_MANY_TABLES 285 +#define MSG_VIEW_ALREADY 286 +#define MSG_VIEW_NOT_IN_DB 287 +#define MSG_WRITE_ERROR 288 +#define MSG_XDB_DEL_ERROR 289 +#define MSG_COL_NOTIN_GRPBY 290 +#define MSG_DOMAIN_ERROR 291 +#define MSG_NO_TOKEN_DB 292 +#define MSG_SPCOL_READONLY 293 +#define MSG_TYPE_VALUE_ERR 294 +#define MSG_UNDEFINED_AM 295 +#define MSG_VALUE_ERROR 296 +#define MSG_SORTING_VAL 297 +#define MSG_NAME_CONV_ERR 298 +#define MSG_WS_CONV_ERR 299 +#define MSG_BAD_COMPARE_OP 300 +#define MSG_BAD_EXP_ARGTYPE 301 +#define MSG_BAD_IN_ARGTYPE 302 +#define MSG_BAD_SUBSEL_IN_X 303 +#define MSG_IN_ARGTYPE_MISM 304 +#define MSG_ROW_ARGNB_ERR 305 +#define MSG_TYPE_CONV_ERROR 306 +#define MSG_NO_MAP_INSERT 307 +#define MSG_SEEK_ERROR 308 +#define MSG_BAD_DBF_FILE 309 +#define MSG_BAD_DBF_REC 310 +#define MSG_BAD_DBF_TYPE 311 +#define MSG_BAD_HEADER 312 +#define MSG_BAD_HEAD_END 313 +#define MSG_BAD_LRECL 314 +#define MSG_BIN_MODE_FAIL 315 +#define MSG_DBASE_FILE 316 +#define MSG_FOXPRO_FILE 317 +#define MSG_NOT_A_DBF_FILE 318 +#define MSG_NO_0DH_HEAD 319 +#define MSG_NO_DBF_INSERT 320 +#define MSG_NO_READ_32 321 +#define MSG_NO_MODE_PADDED 322 +#define MSG_SETEOF_ERROR 323 +#define MSG_REMOVE_ERROR 324 +#define MSG_RENAME_ERROR 325 +#define MSG_BAD_HEADER_VAL 326 +#define MSG_BAD_MAX_NREC 327 +#define MSG_HEAD_OPEN_ERROR 328 +#define MSG_HEAD_READ_ERROR 329 +#define MSG_HEAD_WRITE_ERR 330 +#define MSG_MAP_VEC_ONLY 331 +#define MSG_MKEMPTY_NIY 332 +#define MSG_BAD_BLK_SIZE 333 +#define MSG_BAD_LINE_LEN 334 +#define MSG_FUNC_ERR_S 335 +#define MSG_INV_RAND_ACC 336 +#define MSG_NOP_ZLIB_INDEX 337 +#define MSG_NO_PART_DEL 338 +#define MSG_NO_PAR_BLK_INS 339 +#define MSG_NO_SETPOS_YET 340 +#define MSG_BAD_ARRAY_OPER 341 +#define MSG_BAD_FILTER 342 +#define MSG_BAD_FILTER_CONV 343 +#define MSG_BAD_HAV_FILTYPE 344 +#define MSG_BAD_TYPE_LIKE 345 +#define MSG_ILL_FILTER_CONV 346 +#define MSG_IN_WITHOUT_SUB 347 +#define MSG_UNMATCH_FIL_ARG 348 +#define MSG_VALUE_NOT_ALLOC 349 +#define MSG_VOID_FIRST_ARG 350 +#define MSG_DISTINCT_ERROR 351 +#define MSG_INV_FNC_BUFTYPE 352 +#define MSG_INV_SET_SUBTYPE 353 +#define MSG_NO_AGGR_FUNC 354 +#define MSG_APP_STILL_ACTIV 355 +#define MSG_AREAFILE_NOTFND 356 +#define MSG_BAD_LANG_SIZE 357 +#define MSG_BAD_PARAMETERS 358 +#define MSG_BAD_PARAM_TYPE 359 +#define MSG_GRAM_ALLOC_ERR 360 +#define MSG_GRAM_MISMATCH 361 +#define MSG_GRAM_SUBSET_ERR 362 +#define MSG_INVALID_BIP 363 +#define MSG_LANGUAGE_QUIT 364 +#define MSG_LANG_ALREADY_UP 365 +#define MSG_LANG_BAD_SAVE 366 +#define MSG_LANG_NOT_FREED 367 +#define MSG_LANG_SAVED 368 +#define MSG_LANG_WR_LEN_ERR 369 +#define MSG_LDF_ALLOC_ERROR 370 +#define MSG_LDF_W_LEN_ERROR 371 +#define MSG_LNG_NOT_IN_LIST 372 +#define MSG_METAFILE_NOTFND 373 +#define MSG_NODE_SUBSET_ERR 374 +#define MSG_NO_LANG_TO_QUIT 375 +#define MSG_NO_MORE_LANG 376 +#define MSG_ONE_LANG_YET 377 +#define MSG_ON_LANGUAGE 378 +#define MSG_OPEN_W_ERROR 379 +#define MSG_RULE_SUBSET_ERR 380 +#define MSG_UP_LANGUAGE 381 +#define MSG_VM_LANG 382 +#define MSG_BUILD_INDEX 383 +#define MSG_CD_ORDER_ERROR 384 +#define MSG_DSORT_LOG_ERROR 385 +#define MSG_JOIN_KEY_NO_COL 386 +#define MSG_KEY_ALLOC_ERROR 387 +#define MSG_LOGICAL_ERROR 388 +#define MSG_MEM_ALLOC_ERR 389 +#define MSG_QUERY_CANCELLED 390 +#define MSG_RC_READING 391 +#define MSG_REORDER_INDEX 392 +#define MSG_SORTING_INDEX 393 +#define MSG_SORT_JOIN_INDEX 394 +#define MSG_TOO_MANY_KEYS 395 +#define MSG_TYPE_MISMATCH 396 +#define MSG_REGISTER_ERR 397 +#define MSG_XPATH_CNTX_ERR 398 +#define MSG_XPATH_EVAL_ERR 399 +#define MSG_API_CONF_ERROR 400 +#define MSG_BAD_HANDLE_VAL 401 +#define MSG_COL_NUM_MISM 402 +#define MSG_CONNECT_CANCEL 403 +#define MSG_INV_COLUMN_TYPE 404 +#define MSG_NO_CONNECT_ADDR 405 +#define MSG_NO_TABCOL_DATA 406 +#define MSG_NO_TAB_DATA 407 +#define MSG_SEQUENCE_ERROR 408 +#define MSG_SQL_CONF_ERROR 409 +#define MSG_CONNECTED 410 +#define MSG_CONN_CLOSED 411 +#define MSG_CONN_CREATED 412 +#define MSG_CONN_DROPPED 413 +#define MSG_CONN_OPEN 414 +#define MSG_CONN_SUC_OPEN 415 +#define MSG_DISCONNECTED 416 +#define MSG_IS_NOT_CONN 417 +#define MSG_NAME_IS_USED 418 +#define MSG_NO_SUCH_SERVER 419 +#define MSG_BAD_COLIST_ITEM 420 +#define MSG_BAD_COLIST_TYPE 421 +#define MSG_BAD_COL_IN_FILT 422 +#define MSG_BAD_FUNC_ARG 423 +#define MSG_BAD_FUNC_ARGTYP 424 +#define MSG_BAD_OPERATOR 425 +#define MSG_BAD_PROJNUM 426 +#define MSG_BAD_SEM_DOMAIN 427 +#define MSG_CLN_NOT_IN_JOIN 428 +#define MSG_COLIST_BAD_TYPE 429 +#define MSG_COLUMN_ERROR 430 +#define MSG_COL_NOT_EXIST 431 +#define MSG_COL_NOT_FOUND 432 +#define MSG_COL_NOT_IN_JOIN 433 +#define MSG_DUPLICAT_COUNT 434 +#define MSG_DUP_PROJNUM 435 +#define MSG_FILTER_ATTACH 436 +#define MSG_GRBY_TAB_NOTIMP 437 +#define MSG_NON_DUP_HAVING 438 +#define MSG_NOT_IMPL_JOIN 439 +#define MSG_NOT_IMPL_SET 440 +#define MSG_NO_COL_IN_TABLE 441 +#define MSG_NO_MULT_HAVING 442 +#define MSG_NO_TABLE_DESC 443 +#define MSG_REMOVE_NOT_IMPL 444 +#define MSG_TYPE_TO_VERIFY 445 +#define MSG_UNDEF_COL_COUNT 446 +#define MSG_UNKNOWN_NAME 447 +#define MSG_WRONG_COL_NUM 448 +#define MSG_ALL_DELETED 449 +#define MSG_BAD_LOCALE 450 +#define MSG_BAD_QUERY_TYPE 451 +#define MSG_BUFSIZE_ERROR 452 +#define MSG_END_OF_DELETE 453 +#define MSG_END_OF_INSERT 454 +#define MSG_END_OF_QUERY 455 +#define MSG_END_OF_UPDATE 456 +#define MSG_NCOL_GT_MAXCOL 457 +#define MSG_OPEN_ERROR_IS 458 +#define MSG_SET_LOCALE 459 +#define MSG_X_ARG_ADDED 460 +#define MSG_ALG_CHOICE_AUTO 461 +#define MSG_ALG_CHOICE_BAD 462 +#define MSG_ALG_CHOICE_QRY 463 +#define MSG_ALG_CURLY_BRK 464 +#define MSG_ALTER_DB_ERR 465 +#define MSG_APPL_IS_ACTIVE 466 +#define MSG_APPL_NOT_INIT 467 +#define MSG_AVGLEN_ERROR 468 +#define MSG_BAD_DEF_TYPE 469 +#define MSG_BAD_DIST_JN_FIL 470 +#define MSG_BAD_DIST_JOIN 471 +#define MSG_BAD_DOM_COL_DEF 472 +#define MSG_BAD_FIELD_FMT 473 +#define MSG_BAD_OUTER_JOIN 474 +#define MSG_BAD_SETTINGS 475 +#define MSG_BAD_XMODE_VAL 476 +#define MSG_DATABASE_ACTIVE 477 +#define MSG_DATABASE_LOADED 478 +#define MSG_DB_ALREADY_DEF 479 +#define MSG_DB_ALTERED 480 +#define MSG_DB_CREATED 481 +#define MSG_DB_REMOVED 482 +#define MSG_DB_STOPPED 483 +#define MSG_DIS_NOHEAD_JOIN 484 +#define MSG_DROP_DB_ERR 485 +#define MSG_DUP_COL_NAME 486 +#define MSG_EXECUTING 487 +#define MSG_EXEC_MODE_SET 488 +#define MSG_INDEX_CREATED 489 +#define MSG_INV_DATA_PATH 490 +#define MSG_INV_WORK_PATH 491 +#define MSG_IN_USE 492 +#define MSG_LIC_NO_MYSQL 493 +#define MSG_LINE_LENGTH 494 +#define MSG_LINE_MAXLIN 495 +#define MSG_LINE_MAXRES 496 +#define MSG_LINE_MAXTMP 497 +#define MSG_MAC_WIN_ONLY 498 +#define MSG_MALLOC_NULL 499 +#define MSG_MAP_NO_MORE 500 +#define MSG_MISSING_COL_DEF 501 +#define MSG_MISSING_CONNECT 502 +#define MSG_MISSING_SERV_DB 503 +#define MSG_MISS_NAME_LRECL 504 +#define MSG_MISS_TABLE_LIST 505 +#define MSG_MISS_VCT_ELMT 506 +#define MSG_NEW_CHAR_NULL 507 +#define MSG_NODEF_FROM_VIEW 508 +#define MSG_NO_COL_ADDING 509 +#define MSG_NO_COL_DEF_AS 510 +#define MSG_NO_DATABASE 511 +#define MSG_NO_FULL_JOIN 512 +#define MSG_NO_FUL_OUT_JOIN 513 +#define MSG_NO_HEAD_JOIN 514 +#define MSG_NO_ODBC_COL 515 +#define MSG_NO_SELECTED_DB 516 +#define MSG_NO_SELF_PIVOT 517 +#define MSG_NULL_QUERY 518 +#define MSG_READY 519 +#define MSG_SEC_NOT_FOUND 520 +#define MSG_SET_OP_NOT_IMPL 521 +#define MSG_TABLE_ALTERED 522 +#define MSG_TABLE_CREATED 523 +#define MSG_TABLE_DROPPED 524 +#define MSG_TDB_NXT_NOT_NUL 525 +#define MSG_TYPE_DEF_MISM 526 +#define MSG_TYPE_RECFM_MISM 527 +#define MSG_VIEW_CREATED 528 +#define MSG_VIEW_DROPPED 529 +#define MSG_ANSWER_TYPE 530 +#define MSG_COPY_BAD_PHASE 531 +#define MSG_LIST 532 +#define MSG_MEM_ALLOC_ERROR 533 +#define MSG_PTR_NOT_FOUND 534 +#define MSG_SEMANTIC_TREE 535 +#define MSG_WRONG_TYPE 536 +#define MSG_ARRAY_ERROR 537 +#define MSG_BAD_EVAL_TYPE 538 +#define MSG_BAD_FILTER_LINK 539 +#define MSG_BAD_IN_ENDING 540 +#define MSG_BAD_IN_STRING 541 +#define MSG_BAD_LIST_TYPE 542 +#define MSG_BAD_ORDER_MODE 543 +#define MSG_BAD_ORDER_TYPE 544 +#define MSG_GROUP_ON_FUNC 545 +#define MSG_GRP_LIST_MISMAT 546 +#define MSG_LINEAR_ERROR 547 +#define MSG_NO_FUNC_ORDER 548 +#define MSG_NO_OP_MODIF 549 +#define MSG_NO_TABLE_LIST 550 +#define MSG_ORDER_TWICE 551 +#define MSG_UNKNOWN_ERROR 552 +#define MSG_VOID_IN_STRING 553 +#define MSG_VOID_ORDER_LIST 554 +#define MSG_ACCESS_VIOLATN 555 +#define MSG_ARRAY_BNDS_EXCD 556 +#define MSG_BREAKPOINT 557 +#define MSG_CONTROL_C_EXIT 558 +#define MSG_DATA_MISALIGN 559 +#define MSG_FLT_BAD_RESULT 560 +#define MSG_FLT_DENORMAL_OP 561 +#define MSG_FLT_INVALID_OP 562 +#define MSG_FLT_OVERFLOW 563 +#define MSG_FLT_STACK_CHECK 564 +#define MSG_FLT_UNDERFLOW 565 +#define MSG_FLT_ZERO_DIVIDE 566 +#define MSG_GUARD_PAGE 567 +#define MSG_ILLEGAL_INSTR 568 +#define MSG_INT_OVERFLOW 569 +#define MSG_INT_ZERO_DIVIDE 570 +#define MSG_INVALID_DISP 571 +#define MSG_INVALID_HANDLE 572 +#define MSG_NEW_RETURN_NULL 573 +#define MSG_NONCONT_EXCEPT 574 +#define MSG_NO_ACTIVE_DB 575 +#define MSG_NO_MEMORY 576 +#define MSG_PAGE_ERROR 577 +#define MSG_PARSING_QUERY 578 +#define MSG_PRIV_INSTR 579 +#define MSG_SINGLE_STEP 580 +#define MSG_SQL_BAD_TYPE 581 +#define MSG_UNKNOWN_EXCPT 582 +#define MSG_WRONG_FUNCTION 583 +#define MSG_BAD_RESULT_TYPE 584 +#define MSG_BUFF_TOO_SMALL 585 +#define MSG_COL_ALLOC_ERROR 586 +#define MSG_DATA_IS_NULL 587 +#define MSG_GET_ERROR 588 +#define MSG_INV_COL_DATATYP 589 +#define MSG_INV_INIPATH 590 +#define MSG_NO_NBLIN_CONT 591 +#define MSG_NO_SERVER_FOUND 592 +#define MSG_TYPES_ERROR 593 +#define MSG_UNDEFINED_PATH 594 +#define MSG_UNKNOWN_PATH 595 +#define MSG_WRONG_DB_LIST 596 +#define MSG_BAD_SPECIAL_CMD 597 +#define MSG_CURSOR_SET 598 +#define MSG_EVAL_EXPIRED 599 +#define MSG_EVAL_ONLY 600 +#define MSG_INV_SPECIAL_CMD 601 +#define MSG_PROGRESS_INFO 602 +#define MSG_PROMPT_CANCEL 603 +#define MSG_ARG_ALREADY_SET 604 +#define MSG_BAD_ARG_NUM 605 +#define MSG_BAD_CHECK_VAL 606 +#define MSG_BAD_EXEC_MODE 607 +#define MSG_BAD_MAX_PARAM 608 +#define MSG_BAD_MAX_SETTING 609 +#define MSG_BAD_USETEMP 610 +#define MSG_BAD_USETEMP_VAL 611 +#define MSG_CHECK_LEVEL 612 +#define MSG_COLS_REDUCED 613 +#define MSG_COLUMN_MISMATCH 614 +#define MSG_COL_NOT_IN_DB 615 +#define MSG_DB_NOT_SPEC 616 +#define MSG_DONE 617 +#define MSG_EXEC_MODE_IS 618 +#define MSG_EXEC_MODE_RESET 619 +#define MSG_HUGE_DEFAULT 620 +#define MSG_INDEX_ONE_SAVE 621 +#define MSG_INDEX_SEP_SAVE 622 +#define MSG_INVALID_OPTION 623 +#define MSG_INV_COL_NUM 624 +#define MSG_INV_INFO_TYPE 625 +#define MSG_INV_RESULT_TYPE 626 +#define MSG_LANG_ACTIVE 627 +#define MSG_MAX_BITMAP 628 +#define MSG_MYSQL_CNC_OFF 629 +#define MSG_MYSQL_CNC_ON 630 +#define MSG_MYSQL_NOT_SUP 631 +#define MSG_MY_CNC_ALREADY 632 +#define MSG_NO_AVAIL_RESULT 633 +#define MSG_NO_HQL_CONV 634 +#define MSG_NO_MYSQL_CONN 635 +#define MSG_NO_UNIX_CATINFO 636 +#define MSG_OPENING 637 +#define MSG_PLUG_NOT_RUN 638 +#define MSG_PROMPT_NIY 639 +#define MSG_QUERY_SAVED 640 +#define MSG_REC_SKIPPED 641 +#define MSG_ROWS_SELECTED 642 +#define MSG_ROWS_TRUNCATED 643 +#define MSG_SLEEP 644 +#define MSG_SPEC_CMD_SEP 645 +#define MSG_SYSTEM_ERROR 646 +#define MSG_UNLOADABLE 647 +#define MSG_UNLOADABLE_PRM 648 +#define MSG_USETEMP_IS 649 +#define MSG_USETEMP_RESET 650 +#define MSG_USETEMP_SET 651 +#define MSG_USING_INDEX 652 +#define MSG_VOID_QUERY 653 +#define MSG_WORK_TOO_SMALL 654 +#define MSG_WRITING_QUERY 655 +#define MSG_X_ARG_SET 656 +#define MSG_BAS_NS_LIST 657 +#define MSG_DOM_NOT_SUPP 658 +#define MSG_AFTER 659 +#define MSG_ARG_OUT_CONTEXT 660 +#define MSG_ARG_OUT_RANGE 661 +#define MSG_ARG_PTR_NOSEM 662 +#define MSG_ARG_PTR_NOSEMS 663 +#define MSG_BAD_ARG_TYPE 664 +#define MSG_ERR_RET_RULE 665 +#define MSG_ERR_RET_TYPE 666 +#define MSG_FUNC_REF_DEL 667 +#define MSG_INV_TOPSEM_CMD 668 +#define MSG_NON_EVAL_SEM 669 +#define MSG_N_FULL_PARSES 670 +#define MSG_PARSE_NULL_SEM 671 +#define MSG_PNODE_RULE 672 +#define MSG_SCAN_NOT_IMP 673 +#define MSG_SEM_BAD_REF 674 +#define MSG_SEM_UNKNOWN 675 +#define MSG_SUBARG_NOSEM 676 +#define MSG_SUBARG_OUTRANGE 677 +#define MSG_SYNTAX_ERROR 678 +#define MSG_TOPSEM_ERROR 679 +#define MSG_UNKN_ERR_CODE 680 +#define MSG_ATTRIBUTE_ERROR 681 +#define MSG_BAD_PHRASE_NB 682 +#define MSG_INV_OPERATOR 683 +#define MSG_NO_LANGUAGE 684 +#define MSG_PIX_ERROR 685 +#define MSG_PIX_TEST_ERROR 686 +#define MSG_PLUG_NOT_INIT 687 +#define MSG_STACK_ERROR 688 +#define MSG_STACK_OVERFLOW 689 +#define MSG_APPL_ACCESSIBLE 690 +#define MSG_APPL_BAD_SAVE 691 +#define MSG_APPL_CREATED 692 +#define MSG_APPL_NOT_LOADED 693 +#define MSG_APPL_QUIT 694 +#define MSG_APPL_SAVED 695 +#define MSG_ARG_NOT_AN_ATTR 696 +#define MSG_ATT_NOT_CASE 697 +#define MSG_ATT_POSCODE_BIG 698 +#define MSG_BAD_EDIT_INIT 699 +#define MSG_BAD_FPARM_NEXT 700 +#define MSG_BAD_PHASE_NUM 701 +#define MSG_BAD_SUBLST_TYPE 702 +#define MSG_BAD_USERBLK_LEN 703 +#define MSG_COPY_INV_TYPE 704 +#define MSG_DEBUG_NOT_ACTIV 705 +#define MSG_DEBUG_SET_INV 706 +#define MSG_DICTIONARY 707 +#define MSG_ENDSTR_MISMATCH 708 +#define MSG_ERROR_NO_PARM 709 +#define MSG_EXECUTION_ERROR 710 +#define MSG_FILE_NOT_FOUND 711 +#define MSG_INPUT 712 +#define MSG_INPUT_KEYBD_YET 713 +#define MSG_INV_CONC_BIP 714 +#define MSG_INV_DOMAIN_TYPE 715 +#define MSG_INV_PARAMETER 716 +#define MSG_INV_PARM_TYPE 717 +#define MSG_INV_TRANSF_USE 718 +#define MSG_INV_TYPE_SPEC 719 +#define MSG_LDF_RN_MISMATCH 720 +#define MSG_LDF_WLEN_ERROR 721 +#define MSG_MOVE_INV_TYPE 722 +#define MSG_NODE_FOR_CHAR 723 +#define MSG_NOT_IMPLEMENTED 724 +#define MSG_NOT_IMPL_YET 725 +#define MSG_NOT_MODIFIABLE 726 +#define MSG_NO_ACTIVE_APPL 727 +#define MSG_NO_ACTIVE_UDIC 728 +#define MSG_NO_AREA_FILE 729 +#define MSG_NO_EDITED_LANG 730 +#define MSG_NO_PARAMETER 731 +#define MSG_NO_RCUR_DSK_YET 732 +#define MSG_NO_SOURCE 733 +#define MSG_ONE_PARAM_ONLY 734 +#define MSG_READING_FROM 735 +#define MSG_RESET_TO 736 +#define MSG_STRING_INV_LIST 737 +#define MSG_UNKNOWN_SEM 738 +#define MSG_USED_FREE_MEM 739 +#define MSG_WRONG_PASSWORD 740 +#define MSG_WRONG_USERFILE 741 +#define MSG_ACT_ALLOC_FAIL 742 +#define MSG_APPL_ACTIVE 743 +#define MSG_BAD_CASE_SPEC 744 +#define MSG_DOSALMEM_NOMEM 745 +#define MSG_EXIT_FROM_LANG 746 +#define MSG_GLOBAL_ERROR 747 +#define MSG_HUGE_WARNING_1 748 +#define MSG_HUGE_WARNING_2 749 +#define MSG_LANG_ALLOC_FAIL 750 +#define MSG_MALLOC_ERROR 751 +#define MSG_NO_INIT_LANG 752 +#define MSG_NULL_ENTRY 753 +#define MSG_READ_MEM_ERROR 754 +#define MSG_READ_SEG_ERROR 755 +#define MSG_RECEIVED 756 +#define MSG_RET_FROM_LANG 757 +#define MSG_STRG_NOT_FOUND 758 +#define MSG_SUBALLOC_ERROR 759 +#define MSG_SUBAL_HUGE_ERR 760 +#define MSG_S_ACCESS_DENIED 761 +#define MSG_S_ERROR 762 +#define MSG_S_ERROR_NUM 763 +#define MSG_S_INTRUPT_ERROR 764 +#define MSG_S_INVALID_PARM 765 +#define MSG_S_INV_ADDRESS 766 +#define MSG_S_UNKNOWN_ERROR 767 +#define MSG_VOID_POS_DICT 768 +#define MSG_WORK_AREA 769 +#define MSG_BAD_AGGREG_FUNC 770 +#define MSG_BAD_MAX_HAVING 771 +#define MSG_BUILDING_GROUPS 772 +#define MSG_CD_ONE_STEP 773 +#define MSG_CNTDIS_COL_LOST 774 +#define MSG_COMPUTING 775 +#define MSG_DISTINCT_ROWS 776 +#define MSG_DISTINCT_VALUES 777 +#define MSG_FETCHING_DATA 778 +#define MSG_FETCHING_ROWS 779 +#define MSG_GROUPBY_NOT_ALL 780 +#define MSG_HAVING_FILTER 781 +#define MSG_IDLE 782 +#define MSG_INTERNAL 783 +#define MSG_INV_QUERY_TYPE 784 +#define MSG_MAKING_DISTINCT 785 +#define MSG_MAXTMP_TRUNCATE 786 +#define MSG_MEM_ALLOC_YET 787 +#define MSG_NOT_ENOUGH_MEM 788 +#define MSG_NO_NULL_CONST 789 +#define MSG_OFFSET_NOT_SUPP 790 +#define MSG_OPENING_QUERY 791 +#define MSG_OPEN_SORT_ERROR 792 +#define MSG_PROC_WOULD_LOOP 793 +#define MSG_RSC_ALLOC_ERROR 794 +#define MSG_SMART_SORTING 795 +#define MSG_SMART_SORT_ERR 796 +#define MSG_SORTING 797 +#define MSG_DEF_ALLOC_ERROR 798 +#define MSG_GET_FUNC_ERR 799 +#define MSG_PROCADD_ERROR 800 +#define MSG_PXDEF_IS_NULL 801 +#define MSG_SHARED_LIB_ERR 802 +#define MSG_ADPOS_IN_DICTP 803 +#define MSG_BAD_CHAR_SPEC 804 +#define MSG_BAD_GENRE 805 +#define MSG_BAD_INPUT 806 +#define MSG_BAD_LOCDFON_ARG 807 +#define MSG_BAD_LOCNODE_USE 808 +#define MSG_BAD_POS_CODE 809 +#define MSG_BAD_POS_TYPE 810 +#define MSG_BAD_TYPE_FOR_S 811 +#define MSG_BLOCK_NO_MATCH 812 +#define MSG_BXP_NULL 813 +#define MSG_DIRECT_VARTOK 814 +#define MSG_FSBPARP_NULL 815 +#define MSG_LOCSTRG_TOO_BIG 816 +#define MSG_MISSING_POS 817 +#define MSG_NO_POS_ADDED 818 +#define MSG_NO_TERM_IN_TOK 819 +#define MSG_POS_TOO_LONG 820 +#define MSG_RENUM_RULES 821 +#define MSG_RULE_ENTERED 822 +#define MSG_TOO_MANY_POS 823 +#define MSG_TO_FTR_NOT_NULL 824 +#define MSG_TO_PIX_NOT_NULL 825 +#define MSG_TO_SEM_NOT_NULL 826 +#define MSG_UNKNOWN_POS 827 +#define MSG_UNKNOWN_SYNONYM 828 +#define MSG_USE_NO_MATCH 829 +#define MSG_ALLOC_ERROR 830 +#define MSG_ARG_TWO_CONST 831 +#define MSG_BAD_ARGTYPES 832 +#define MSG_BAD_ARGUMENTS 833 +#define MSG_BAD_ROW_VALIST 834 +#define MSG_BAD_ROW_VALNB 835 +#define MSG_BAD_SCF_ARGTYPE 836 +#define MSG_BAD_SUB_SELECT 837 +#define MSG_BAD_TYPE_FOR_IN 838 +#define MSG_CONNECT_ERROR 839 +#define MSG_EXIT_EVAL_ERR 840 +#define MSG_FORMAT_ERROR 841 +#define MSG_INIT_ERROR 842 +#define MSG_INVALID_OPER 843 +#define MSG_NO_SFEXIT_UNIX 844 +#define MSG_READING_RECORD 845 +#define MSG_REQU_ARG_NUM 846 +#define MSG_SFUNC_NOT_IMPL 847 +#define MSG_UNKNOWN_DOMAIN 848 +#define MSG_WRONG_ARG_NUM 849 +#define MSG_WRONG_OP_PARM 850 +#define MSG_WRONG_PARMS 851 +#define MSG_AMBIG_CORREL 852 +#define MSG_BAD_CHECK_TYPE 853 +#define MSG_BAD_CORREL 854 +#define MSG_BAD_XOBJ_TYPE 855 +#define MSG_HBUF_TOO_SMALL 856 +#define MSG_MISSING 857 +#define MSG_MULT_DISTINCT 858 +#define MSG_NO_TABLE_COL 859 +#define MSG_ARG_REF_LOOP 860 +#define MSG_GETCWD_ERR_NO 861 +#define MSG_UNRESOLVED_ARG 862 +#define MSG_ARGS_SYNTAX_ERR 863 +#define MSG_AMBIG_COL_QUAL 864 +#define MSG_AMBIG_SPEC_COL 865 +#define MSG_BAD_COL_QUALIF 866 +#define MSG_BAD_FETCH_RC 867 +#define MSG_BAD_FILTER_OP 868 +#define MSG_BAD_HAV_FILTER 869 +#define MSG_BAD_JOIN_FILTER 870 +#define MSG_BAD_SET_TYPE 871 +#define MSG_BAD_SPECIAL_COL 872 +#define MSG_BAD_SPEC_COLUMN 873 +#define MSG_BAD_SQL_PARAM 874 +#define MSG_BAD_TABLE_LIST 875 +#define MSG_BAD_UPD_COR 876 +#define MSG_BAD_VALUE_TYPE 877 +#define MSG_BUILD_DIST_GRPS 878 +#define MSG_CHECKING_ROWS 879 +#define MSG_COL_INVAL_TABLE 880 +#define MSG_COL_ISNOT_TABLE 881 +#define MSG_COL_NOTIN_TABLE 882 +#define MSG_COL_NOTIN_UPDT 883 +#define MSG_COMPUTING_DIST 884 +#define MSG_COMPUTING_FUNC 885 +#define MSG_DELETING_ROWS 886 +#define MSG_ERROR 887 +#define MSG_FILGRP_NO_TABLE 888 +#define MSG_FILTER_NO_TABLE 889 +#define MSG_INSERTING 890 +#define MSG_INSERT_MISMATCH 891 +#define MSG_INV_FILTER 892 +#define MSG_INV_UPDT_TABLE 893 +#define MSG_INV_VALUE_LIST 894 +#define MSG_INV_WHERE_JOIN 895 +#define MSG_NOT_LINEARIZED 896 +#define MSG_NO_CONST_FILTER 897 +#define MSG_NO_INDEX_GBX 898 +#define MSG_READB_BAD_INIT 899 +#define MSG_READING 900 +#define MSG_SEVERAL_TREES 901 +#define MSG_UNKNW_QRY_TYPE 902 +#define MSG_UNQ_COL_SEV_TAB 903 +#define MSG_UPDATING_ROWS 904 +#define MSG_VAL_TOO_LONG 905 +#define MSG_BAD_FILTEST_OP 906 +#define MSG_BAD_SUBSEL_TYPE 907 +#define MSG_BAD_SUB_RESULT 908 +#define MSG_CORREL_NO_QRY 909 +#define MSG_FLTST_NO_CORREL 910 +#define MSG_NO_MEM_CORR_SUB 911 +#define MSG_NO_QUERY_ARRAY 912 +#define MSG_PROCESS_SUBQRY 913 +#define MSG_READ_ERROR_RC 914 +#define MSG_RES_NOT_UNIQUE 915 +#define MSG_SQL_BLOCK_MISM 916 +#define MSG_SUBQRY_ONEITEM 917 +#define MSG_SUB_OPEN_YET 918 +#define MSG_FNC_NOTIN_SLIST 919 +#define MSG_NO_FORMAT_COL 920 +#define MSG_ORDER_OUT_RANGE 921 +#define MSG_SEP_IN_FIELD 922 +#define MSG_BAD_OPEN_MODE 923 +#define MSG_OPEN_MODE_ERROR 924 +#define MSG_RECORD_NO_SEP 925 +#define MSG_BAD_BLK_ESTIM 926 +#define MSG_BAD_FREQ_SET 927 +#define MSG_BAD_RECFM 928 +#define MSG_BAD_RECFM_VAL 929 +#define MSG_BIN_F_TOO_LONG 930 +#define MSG_CHSIZE_ERROR 931 +#define MSG_DEL_FILE_ERR 932 +#define MSG_DEL_READ_ERROR 933 +#define MSG_DEL_WRITE_ERROR 934 +#define MSG_DVAL_NOTIN_LIST 935 +#define MSG_FILELEN_ERROR 936 +#define MSG_FILE_IS_EMPTY 937 +#define MSG_FILE_MAP_ERROR 938 +#define MSG_FPUTS_ERROR 939 +#define MSG_FSEEK_ERROR 940 +#define MSG_FSETPOS_ERROR 941 +#define MSG_FTELL_ERROR 942 +#define MSG_FUNCTION_ERROR 943 +#define MSG_GETFILESIZE_ERR 944 +#define MSG_GET_DIST_VALS 945 +#define MSG_INDEX_NOT_DEF 946 +#define MSG_INDEX_YET_ON 947 +#define MSG_INDX_COL_NOTIN 948 +#define MSG_INDX_EXIST_YET 949 +#define MSG_INSERT_ERROR 950 +#define MSG_INV_DEF_READ 951 +#define MSG_INV_MAP_POS 952 +#define MSG_MAP_VIEW_ERROR 953 +#define MSG_NOT_FIXED_LEN 954 +#define MSG_NO_INDEX_IN 955 +#define MSG_NO_RECOV_SPACE 956 +#define MSG_NO_ROWID_FOR_AM 957 +#define MSG_OPEN_STRERROR 958 +#define MSG_OPTBLK_RD_ERR 959 +#define MSG_OPTBLK_WR_ERR 960 +#define MSG_OPT_BMAP_RD_ERR 961 +#define MSG_OPT_BMAP_WR_ERR 962 +#define MSG_OPT_DVAL_RD_ERR 963 +#define MSG_OPT_DVAL_WR_ERR 964 +#define MSG_OPT_LOGIC_ERR 965 +#define MSG_READ_ERROR 966 +#define MSG_READ_SEEK_ERROR 967 +#define MSG_TABLE_NOT_OPT 968 +#define MSG_TRUNCATE_ERROR 969 +#define MSG_VALUE_TOO_LONG 970 +#define MSG_WRITE_SEEK_ERR 971 +#define MSG_BAD_BIN_FMT 972 +#define MSG_BAD_DEF_READ 973 +#define MSG_COL_NOT_SORTED 974 +#define MSG_ERROR_IN_LSK 975 +#define MSG_ERROR_IN_SFP 976 +#define MSG_FILE_OPEN_YET 977 +#define MSG_FWRITE_ERROR 978 +#define MSG_INVALID_FTYPE 979 +#define MSG_INV_REC_POS 980 +#define MSG_NO_BIG_DELETE 981 +#define MSG_NO_CLUSTER_COL 982 +#define MSG_OPEN_ERROR 983 +#define MSG_OPTIMIZING 984 +#define MSG_OPT_CANCELLED 985 +#define MSG_OPT_HEAD_RD_ERR 986 +#define MSG_OPT_HEAD_WR_ERR 987 +#define MSG_OPT_MAX_RD_ERR 988 +#define MSG_OPT_MAX_WR_ERR 989 +#define MSG_OPT_MIN_RD_ERR 990 +#define MSG_OPT_MIN_WR_ERR 991 +#define MSG_OPT_NOT_MATCH 992 +#define MSG_VALUE_TOO_BIG 993 +#define MSG_WRITING_ERROR 994 +#define MSG_BAD_FIELD_RANK 995 +#define MSG_BAD_FLD_FORMAT 996 +#define MSG_BAD_FLD_LENGTH 997 +#define MSG_BAD_LINEFLD_FMT 998 +#define MSG_BAD_QUOTE_FIELD 999 +#define MSG_CANNOT_OPEN 1000 +#define MSG_EOF_AFTER_LINE 1001 +#define MSG_ERR_READING_REC 1002 +#define MSG_FIELD_TOO_LONG 1003 +#define MSG_FLD_TOO_LNG_FOR 1004 +#define MSG_FMT_WRITE_NIY 1005 +#define MSG_LINE_TOO_LONG 1006 +#define MSG_LRECL_TOO_SMALL 1007 +#define MSG_MISPLACED_QUOTE 1008 +#define MSG_MISSING_EOL 1009 +#define MSG_MISSING_FIELD 1010 +#define MSG_MISSING_FNAME 1011 +#define MSG_NO_FLD_FORMAT 1012 +#define MSG_QUOTE_IN_QUOTE 1013 +#define MSG_TOO_MANY_FIELDS 1014 +#define MSG_UNBALANCE_QUOTE 1015 +#define MSG_BAD_JCOL_TYPE 1016 +#define MSG_COL_USED_TWICE 1017 +#define MSG_DUMMY_NO_COLS 1018 +#define MSG_JCT_MISS_COLS 1019 +#define MSG_JCT_MISS_TABLE 1020 +#define MSG_JCT_NO_FILTER 1021 +#define MSG_JCT_NO_KEY 1022 +#define MSG_NO_DMY_DIR_ACC 1023 +#define MSG_NO_EXP_LINK 1024 +#define MSG_VIR_NO_DELETE 1025 +#define MSG_VIR_READ_ONLY 1026 +#define MSG_BAD_JOIN_EXP 1027 +#define MSG_BAD_JOIN_OP 1028 +#define MSG_COLUMN_NOT_KEY 1029 +#define MSG_DB_SORT_ERROR 1030 +#define MSG_LINJOINDB_ERROR 1031 +#define MSG_MULT_KEY_ERROR 1032 +#define MSG_NO_JOIN_TO_EXP 1033 +#define MSG_NO_MULCOL_JOIN 1034 +#define MSG_READ_ONLY 1035 +#define MSG_ROWID_NOT_IMPL 1036 +#define MSG_SETRECPOS_NIY 1037 +#define MSG_TABLE_MULT_JOIN 1038 +#define MSG_TDB_USE_ERROR 1039 +#define MSG_BAD_CARDINALITY 1040 +#define MSG_BAD_DIRECTORY 1041 +#define MSG_BAD_FILE_HANDLE 1042 +#define MSG_INV_DIRCOL_OFST 1043 +#define MSG_MAXSIZE_ERROR 1044 +#define MSG_MUL_MAKECOL_ERR 1045 +#define MSG_NEXT_FILE_ERROR 1046 +#define MSG_NO_DIR_INDX_RD 1047 +#define MSG_NO_INDEX_READ 1048 +#define MSG_NO_MUL_DIR_ACC 1049 +#define MSG_SRCH_CLOSE_ERR 1050 +#define MSG_TABDIR_READONLY 1051 +#define MSG_TABMUL_READONLY 1052 +#define MSG_NO_EXT_FILTER 1053 +#define MSG_NO_EXT_UPDATE 1054 +#define MSG_NO_ODBC_DELETE 1055 +#define MSG_NO_ODBC_DIRECT 1056 +#define MSG_NO_ODBC_MUL 1057 +#define MSG_NO_ODBC_SPECOL 1058 +#define MSG_NO_UPDEL_JOIN 1059 +#define MSG_ODBC_READ_ONLY 1060 +#define MSG_PARM_CNT_MISS 1061 +#define MSG_COLNAM_TOO_LONG 1062 +#define MSG_NOT_ENOUGH_COLS 1063 +#define MSG_NO_DEF_FNCCOL 1064 +#define MSG_NO_DEF_PIVOTCOL 1065 +#define MSG_NO_MATCH_COL 1066 +#define MSG_NO_MORE_COL 1067 +#define MSG_NO_PIV_DIR_ACC 1068 +#define MSG_NO_TABLE_DEL 1069 +#define MSG_SRC_TABLE_UNDEF 1070 +#define MSG_TABLE_READ_ONLY 1071 +#define MSG_TOO_MANY_COLS 1072 +#define MSG_BAD_COLDEF_TYPE 1073 +#define MSG_BAD_MERGE_TYPE 1074 +#define MSG_COL_NB_MISM 1075 +#define MSG_ERROR_OPENING 1076 +#define MSG_FETCH_NO_RES 1077 +#define MSG_LOAD_CDLL_ERROR 1078 +#define MSG_NO_NBCOL 1079 +#define MSG_NO_NBLIN 1080 +#define MSG_NO_PROMPTING 1081 +#define MSG_NO_REMOTE_FNC 1082 +#define MSG_NO_VIEW_COLDEF 1083 +#define MSG_PLG_READ_ONLY 1084 +#define MSG_PLM_NULL_SFP 1085 +#define MSG_QUERY_NOT_EXEC 1086 +#define MSG_REMOTE_CONN_ERR 1087 +#define MSG_RPC_SERVER_ERR 1088 +#define MSG_BAD_QUERY_OPEN 1089 +#define MSG_BAD_RETURN_TYPE 1090 +#define MSG_BAD_VIEW_OPEN 1091 +#define MSG_NO_QRY_DELETE 1092 +#define MSG_NO_SQL_DELETE 1093 +#define MSG_NO_VIEW_SORT 1094 +#define MSG_NULL_COL_VALUE 1095 +#define MSG_QRY_READ_ONLY 1096 +#define MSG_READCOL_ERROR 1097 +#define MSG_SQL_READ_ONLY 1098 +#define MSG_GET_NAME_ERR 1099 +#define MSG_INV_SUBTYPE 1100 +#define MSG_NO_CURLY_BRKT 1101 +#define MSG_NO_KEY_UPDATE 1102 +#define MSG_NO_SECTION_NAME 1103 +#define MSG_NO_SEC_UPDATE 1104 +#define MSG_SEC_KEY_FIRST 1105 +#define MSG_SEC_NAME_FIRST 1106 +#define MSG_NO_MATCHING_COL 1107 +#define MSG_BAD_BYTE_NUM 1108 +#define MSG_BAD_BYTE_READ 1109 +#define MSG_BAD_READ_NUMBER 1110 +#define MSG_BLK_IS_NULL 1111 +#define MSG_EMPTY_FILE 1112 +#define MSG_MAKE_EMPTY_FILE 1113 +#define MSG_MAKING 1114 +#define MSG_NO_VCT_DELETE 1115 +#define MSG_OPEN_EMPTY_FILE 1116 +#define MSG_OPT_INIT 1117 +#define MSG_SFP_ERROR 1118 +#define MSG_TO_BLK_IS_NULL 1119 +#define MSG_TRUNC_BY_ESTIM 1120 +#define MSG_UPDATE_ERROR 1121 +#define MSG_WRITE_STRERROR 1122 +#define MSG_WRITING 1123 +#define MSG_BAD_COL_XPATH 1124 +#define MSG_BAD_NODE_TYPE 1125 +#define MSG_BAD_VALNODE 1126 +#define MSG_BAD_VAL_UPDATE 1127 +#define MSG_COL_ALLOC_ERR 1128 +#define MSG_COM_ERROR 1129 +#define MSG_CONCAT_SUBNODE 1130 +#define MSG_CREATED_PLUGDB 1131 +#define MSG_DEPREC_FLAG 1132 +#define MSG_EMPTY_DOC 1133 +#define MSG_FAIL_ADD_NODE 1134 +#define MSG_FILE_UNFOUND 1135 +#define MSG_INIT_FAILED 1136 +#define MSG_INV_COL_TYPE 1137 +#define MSG_LOADING_FAILED 1138 +#define MSG_MISSING_NODE 1139 +#define MSG_MISSING_ROWNODE 1140 +#define MSG_MIS_TAG_LIST 1141 +#define MSG_NEW_DOC_FAILED 1142 +#define MSG_NO_ROW_NODE 1143 +#define MSG_VAL_ALLOC_ERR 1144 +#define MSG_XMLTAB_INIT_ERR 1145 +#define MSG_XML_INIT_ERROR 1146 +#define MSG_XPATH_NOT_SUPP 1147 +#define MSG_DLL_LOAD_ERROR 1148 +#define MSG_GZOPEN_ERROR 1149 +#define MSG_GZPUTS_ERROR 1150 +#define MSG_NO_ZIP_DELETE 1151 +#define MSG_NO_ZIP_DIR_ACC 1152 +#define MSG_UPD_ZIP_NOT_IMP 1153 +#define MSG_MAC_NO_DELETE 1154 +#define MSG_MAC_NO_INDEX 1155 +#define MSG_MAC_READ_ONLY 1156 +#define MSG_BAD_FIELD_TYPE 1157 +#define MSG_BAD_PARM_COUNT 1158 +#define MSG_NO_JOIN_UPDEL 1159 +#define MSG_NO_MYSQL_DELETE 1160 +#define MSG_NO_REF_DELETE 1161 +#define MSG_NO_REF_UPDATE 1162 +#define MSG_NO_SPEC_COL 1163 +#define MSG_ADD_NULL_DOM 1164 +#define MSG_BAD_DOM_VALUE 1165 +#define MSG_BAD_SET_STRING 1166 +#define MSG_BAD_VALBLK_INDX 1167 +#define MSG_BAD_VALBLK_TYPE 1168 +#define MSG_COMPUTE_NIY 1169 +#define MSG_DOMAIN_EMPTY 1170 +#define MSG_DOMAIN_FULL 1171 +#define MSG_DOM_FILE_ERROR 1172 +#define MSG_DOM_OPEN_ERROR 1173 +#define MSG_DOM_READ_ERROR 1174 +#define MSG_DOM_READ_ONLY 1175 +#define MSG_DOM_WRITE_ERROR 1176 +#define MSG_ERR_NUM_GT_MAX 1177 +#define MSG_INV_TOK_DOMAIN 1178 +#define MSG_MEMSIZE_TOO_BIG 1179 +#define MSG_NO_DOM_DELETE 1180 +#define MSG_NO_DOM_MATCH 1181 +#define MSG_SET_NULL_DOM 1182 +#define MSG_STRING_TOO_BIG 1183 +#define MSG_VALTYPE_NOMATCH 1184 +#define MSG_NO_DATE_FMT 1185 +#define MSG_NO_LISTVAL_HERE 1186 +#define MSG_BAD_COL_FORMAT 1187 +#define MSG_BAD_DATETIME 1188 +#define MSG_BAD_DATE_OPER 1189 +#define MSG_BAD_EXP_OPER 1190 +#define MSG_BAD_PAD_ARGTYP 1191 +#define MSG_BAD_TRIM_ARGTYP 1192 +#define MSG_BLKTYPLEN_MISM 1193 +#define MSG_COMPUTE_ERROR 1194 +#define MSG_FIX_OVFLW_ADD 1195 +#define MSG_FIX_OVFLW_TIMES 1196 +#define MSG_FIX_UNFLW_ADD 1197 +#define MSG_FIX_UNFLW_TIMES 1198 +#define MSG_HARRY_COMP_NIY 1199 +#define MSG_NO_CHAR_FROM 1200 +#define MSG_NO_FORMAT_TYPE 1201 +#define MSG_ONLY_LOG10_IMPL 1202 +#define MSG_OP_RES_TOO_LONG 1203 +#define MSG_SET_STR_TRUNC 1204 +#define MSG_SUB_RES_TOO_LNG 1205 +#define MSG_VALIST_MISMATCH 1206 +#define MSG_VALSTR_TOO_LONG 1207 +#define MSG_ZERO_DIVIDE 1208 +#define MSG_ADDVAL_ERROR 1209 +#define MSG_ARRAY_ALLOC_ERR 1210 +#define MSG_BAD_DEF_ARG 1211 +#define MSG_BAD_FUNC_MODE 1212 +#define MSG_BAD_INDEX_COL 1213 +#define MSG_BAD_INDEX_DEF 1214 +#define MSG_BAD_INDEX_FILE 1215 +#define MSG_BAD_INDEX_PART 1216 +#define MSG_EOF_INDEX_FILE 1217 +#define MSG_FILE_CLOSE_ERR 1218 +#define MSG_FILE_MAP_ERR 1219 +#define MSG_FUNC_ERRNO 1220 +#define MSG_FUNC_ERROR 1221 +#define MSG_GRP_COL_MISM 1222 +#define MSG_HANDLE_IS_NULL 1223 +#define MSG_HI_OFFSET_ERR 1224 +#define MSG_INDEX_DEF_ERR 1225 +#define MSG_INDEX_INIT_ERR 1226 +#define MSG_INDEX_NOT_UNIQ 1227 +#define MSG_INT_COL_ERROR 1228 +#define MSG_KEY_ALLOC_ERR 1229 +#define MSG_MAP_OBJ_ERR 1230 +#define MSG_MISS_LEAD_COL 1231 +#define MSG_NEW_TABLE_ERR 1232 +#define MSG_NO_KEY_COL 1233 +#define MSG_NO_PART_MAP 1234 +#define MSG_NUMVAL_NOMATCH 1235 +#define MSG_RANGE_NIY 1236 +#define MSG_RANGE_NO_JOIN 1237 +#define MSG_REDUCE_INDEX 1238 +#define MSG_SAVING_INDEX 1239 +#define MSG_TABLE_NO_INDEX 1240 +#define MSG_XCOL_MISMATCH 1241 +#define MSG_XFILE_READERR 1242 +#define MSG_XFILE_TOO_SMALL 1243 +#define MSG_XFILE_WRITERR 1244 +#define MSG_ADD_BAD_TYPE 1245 +#define MSG_BAD_ARRAY_TYPE 1246 +#define MSG_BAD_CONST_TYPE 1247 +#define MSG_BAD_CONV_TYPE 1248 +#define MSG_BAD_FLOAT_CONV 1249 +#define MSG_BAD_TEST_TYPE 1250 +#define MSG_FIND_BAD_TYPE 1251 diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc new file mode 100644 index 00000000000..4db70efe27f --- /dev/null +++ b/storage/connect/mycat.cc @@ -0,0 +1,700 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*************** Mycat CC Program Source Code File (.CC) ***************/ +/* PROGRAM NAME: MYCAT */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* Author: Olivier Bertrand 2012 - 2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the DB description related routines. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#if defined(WIN32) +//#include <windows.h> +//#include <sqlext.h> +#elif defined(UNIX) +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#endif +#define DONT_DEFINE_VOID +//#include <mysql/plugin.h> +#include "handler.h" +#undef OFFSET + +/***********************************************************************/ +/* Include application header files */ +/* */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing DB application declarations. */ +/* tabdos.h is header containing TDBDOS classes declarations. */ +/* MYCAT.h is header containing DB description declarations. */ +/***********************************************************************/ +#if defined(UNIX) +#include "osutil.h" +#endif // UNIX +#include "global.h" +#include "plgdbsem.h" +#include "reldef.h" +#include "tabcol.h" +#include "xtable.h" +#include "filamtxt.h" +#include "tabdos.h" +#include "tabfmt.h" +#include "tabvct.h" +#include "tabsys.h" +#if defined(WIN32) +#include "tabmac.h" +#include "tabwmi.h" +#endif // WIN32 +#include "tabtbl.h" +#if defined(XML_SUPPORT) +#include "tabxml.h" +#endif // XML_SUPPORT +#include "tabmul.h" +#if defined(MYSQL_SUPPORT) +#include "tabmysql.h" +#endif // MYSQL_SUPPORT +#if defined(ODBC_SUPPORT) +#define NODBC +#include "tabodbc.h" +#endif // ODBC_SUPPORT +#if defined(PIVOT_SUPPORT) +#include "tabpivot.h" +#endif // PIVOT_SUPPORT +#include "ha_connect.h" +#include "mycat.h" + +/***********************************************************************/ +/* Extern static variables. */ +/***********************************************************************/ +#if defined(WIN32) +extern "C" HINSTANCE s_hModule; // Saved module handle +#endif // !WIN32 + +extern int xtrace; + +/***********************************************************************/ +/* Get a unique enum table type ID. */ +/***********************************************************************/ +TABTYPE GetTypeID(const char *type) + { + return (!type) ? TAB_UNDEF + : (!stricmp(type, "DOS")) ? TAB_DOS + : (!stricmp(type, "FIX")) ? TAB_FIX + : (!stricmp(type, "BIN")) ? TAB_BIN + : (!stricmp(type, "CSV")) ? TAB_CSV + : (!stricmp(type, "FMT")) ? TAB_FMT + : (!stricmp(type, "DBF")) ? TAB_DBF +#ifdef XML_SUPPORT + : (!stricmp(type, "XML")) ? TAB_XML +#endif + : (!stricmp(type, "INI")) ? TAB_INI + : (!stricmp(type, "VEC")) ? TAB_VEC +#ifdef ODBC_SUPPORT + : (!stricmp(type, "ODBC")) ? TAB_ODBC +#endif +#ifdef MYSQL_SUPPORT + : (!stricmp(type, "MYSQL")) ? TAB_MYSQL +#endif + : (!stricmp(type, "DIR")) ? TAB_DIR +#ifdef WIN32 + : (!stricmp(type, "MAC")) ? TAB_MAC + : (!stricmp(type, "WMI")) ? TAB_WMI +#endif + : (!stricmp(type, "TBL")) ? TAB_TBL + : (!stricmp(type, "OEM")) ? TAB_OEM : TAB_NIY; + } // end of GetTypeID + +/***********************************************************************/ +/* Return true for table types based on file. */ +/***********************************************************************/ +bool IsFileType(TABTYPE type) + { + bool isfile; + + switch (type) { + case TAB_DOS: + case TAB_FIX: + case TAB_BIN: + case TAB_CSV: + case TAB_FMT: + case TAB_DBF: + case TAB_XML: + case TAB_INI: + case TAB_VEC: + isfile= true; + break; + default: + isfile= false; + break; + } // endswitch type + + return isfile; + } // end of IsFileType + +/***********************************************************************/ +/* Return true for table types accepting null fields. */ +/***********************************************************************/ +bool IsTypeNullable(TABTYPE type) + { + bool nullable; + +#if 0 + switch (type) { + case TAB_ODBC: + case TAB_MYSQL: + case TAB_TBL: + case TAB_INI: + case TAB_XML: + nullable= true; + break; + default: + nullable= false; + break; + } // endswitch type +#endif // 0 + + switch (type) { + case TAB_MAC: + case TAB_DIR: + nullable= false; + break; + default: + nullable= true; + break; + } // endswitch type + + + return nullable; + } // end of IsTypeNullable + +/***********************************************************************/ +/* Return true for table types with fix length records. */ +/***********************************************************************/ +bool IsTypeFixed(TABTYPE type) + { + bool fix; + + switch (type) { + case TAB_FIX: + case TAB_BIN: + case TAB_VEC: +// case TAB_DBF: ??? + fix= true; + break; + default: + fix= false; + break; + } // endswitch type + + return fix; + } // end of IsTypeFixed + +/***********************************************************************/ +/* Get a unique enum catalog function ID. */ +/***********************************************************************/ +uint GetFuncID(const char *func) + { + uint fnc; + + if (!func) + fnc= FNC_NO; + else if (!strnicmp(func, "col", 3)) + fnc= FNC_COL; + else if (!strnicmp(func, "tab", 3)) + fnc= FNC_TABLE; + else if (!stricmp(func, "dsn") || + !strnicmp(func, "datasource", 10) || + !strnicmp(func, "source", 6) || + !strnicmp(func, "sqldatasource", 13)) + fnc= FNC_DSN; + else if (!strnicmp(func, "driver", 6) || + !strnicmp(func, "sqldriver", 9)) + fnc= FNC_DRIVER; + else + fnc= FNC_NIY; + + return fnc; + } // end of GetFuncID + +/* ------------------------- Class CATALOG --------------------------- */ + +/***********************************************************************/ +/* CATALOG Constructor. */ +/***********************************************************************/ +CATALOG::CATALOG(void) + { + To_Desc= NULL; +//*DescFile= '\0'; +#if defined(WIN32) + DataPath= ".\\"; +#else // !WIN32 + DataPath= "./"; +#endif // !WIN32 + Descp= NULL; +//memset(&DescArea, 0, sizeof(AREADEF)); + memset(&Ctb, 0, sizeof(CURTAB)); + Cbuf= NULL; + Cblen= 0; + DefHuge= false; + } // end of CATALOG constructor + +/* -------------------------- Class MYCAT ---------------------------- */ + +/***********************************************************************/ +/* MYCAT Constructor. */ +/***********************************************************************/ +MYCAT::MYCAT(PHC hc) : CATALOG() + { + Hc= hc; + To_Desc= NULL; + DefHuge= false; +//SepIndex= true; // Temporay until we can store offet and size + } // end of MYCAT constructor + +/***********************************************************************/ +/* When using volatile storage, reset values pointing to Sarea. */ +/***********************************************************************/ +void MYCAT::Reset(void) + { + To_Desc= NULL; + } // end of Reset + +/***********************************************************************/ +/* This function sets the current database path. */ +/***********************************************************************/ +void MYCAT::SetPath(PGLOBAL g, LPCSTR *datapath, const char *path) + { + if (path) { + size_t len= strlen(path) + (*path != '.' ? 4 : 1); + char *buf= (char*)PlugSubAlloc(g, NULL, len); + + if (*path != '.') { +#if defined(WIN32) + char *s= "\\"; +#else // !WIN32 + char *s= "/"; +#endif // !WIN32 + strcat(strcat(strcat(strcpy(buf, "."), s), path), s); + } else + strcpy(buf, path); + + *datapath= buf; + } // endif path + + } // end of SetDataPath + +/***********************************************************************/ +/* This function sets an integer MYCAT information. */ +/***********************************************************************/ +bool MYCAT::SetIntCatInfo(PSZ what, int n) + { + return Hc->SetIntegerOption(what, n); + } // end of SetIntCatInfo + +/***********************************************************************/ +/* This function returns integer MYCAT information. */ +/***********************************************************************/ +int MYCAT::GetIntCatInfo(PSZ what, int idef) + { + int n= Hc->GetIntegerOption(what); + + return (n == NO_IVAL) ? idef : n; + } // end of GetIntCatInfo + +/***********************************************************************/ +/* This function returns Boolean MYCAT information. */ +/***********************************************************************/ +bool MYCAT::GetBoolCatInfo(PSZ what, bool bdef) + { + bool b= Hc->GetBooleanOption(what, bdef); + + return b; + } // end of GetBoolCatInfo + +/***********************************************************************/ +/* This function returns size catalog information. */ +/***********************************************************************/ +int MYCAT::GetSizeCatInfo(PSZ what, PSZ sdef) + { + char * s, c; + int i, n= 0; + + if (!(s= Hc->GetStringOption(what))) + s= sdef; + + if ((i= sscanf(s, " %d %c ", &n, &c)) == 2) + switch (toupper(c)) { + case 'M': + n *= 1024; + case 'K': + n *= 1024; + } // endswitch c + + return n; +} // end of GetSizeCatInfo + +/***********************************************************************/ +/* This function sets char MYCAT information in buf. */ +/***********************************************************************/ +int MYCAT::GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size) + { + char *s= Hc->GetStringOption(what); + + strncpy(buf, ((s) ? s : sdef), size); + return size; + } // end of GetCharCatInfo + +/***********************************************************************/ +/* This function returns string MYCAT information. */ +/* Default parameter is "*" to get the handler default. */ +/***********************************************************************/ +char *MYCAT::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef) + { + char *sval, *s= Hc->GetStringOption(what, sdef); + + if (s) { + sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1); + strcpy(sval, s); + } else if (!stricmp(what, "filename")) { + // Return default file name + char *ftype= Hc->GetStringOption("Type", "dos"); + int i, n; + + sval= (char*)PlugSubAlloc(g, NULL, strlen(Hc->GetTableName()) + 12); + strcat(strcpy(sval, Hc->GetTableName()), "."); + n= strlen(sval); + + // Fold ftype to lower case + for (i= 0; i < 12; i++) + if (!ftype[i]) { + sval[n+i]= 0; + break; + } else + sval[n+i]= tolower(ftype[i]); + + } else + sval = NULL; + + return sval; + } // end of GetStringCatInfo + +/***********************************************************************/ +/* This function returns column MYCAT information. */ +/***********************************************************************/ +int MYCAT::GetColCatInfo(PGLOBAL g, PTABDEF defp) + { + char *type= GetStringCatInfo(g, "Type", "DOS"); + int i, loff, poff, nof, nlg; + void *field= NULL; + TABTYPE tc; + PCOLDEF cdp, lcdp= NULL, tocols= NULL; + PCOLINFO pcf= (PCOLINFO)PlugSubAlloc(g, NULL, sizeof(COLINFO)); + + // Get a unique char identifier for type + tc= (defp->Catfunc == FNC_NO) ? GetTypeID(type) : TAB_CATLG; + + // Take care of the column definitions + i= poff= nof= nlg= 0; + + // Offsets of HTML and DIR tables start from 0, DBF at 1 + loff= (tc == TAB_DBF) ? 1 : (tc == TAB_XML || tc == TAB_DIR) ? -1 : 0; + + while (true) { + // Default Offset depends on table type + switch (tc) { + case TAB_DOS: + case TAB_FIX: + case TAB_BIN: + case TAB_VEC: + case TAB_DBF: + poff= loff + nof; // Default next offset + nlg= max(nlg, poff); // Default lrecl + break; + case TAB_CSV: + case TAB_FMT: + nlg+= nof; + case TAB_DIR: + case TAB_XML: + poff= loff + 1; + break; + case TAB_INI: + case TAB_MAC: + case TAB_TBL: + case TAB_OEM: + poff = 0; // Offset represents an independant flag + break; + default: // VCT PLG ODBC MYSQL WMI... + poff = 0; // NA + break; + } // endswitch tc + + do { + field= Hc->GetColumnOption(field, pcf); + } while (field && (*pcf->Name =='*' /*|| pcf->Flags & U_VIRTUAL*/)); + + if (tc == TAB_DBF && pcf->Type == TYPE_DATE && !pcf->Datefmt) { + // DBF date format defaults to 'YYYMMDD' + pcf->Datefmt= "YYYYMMDD"; + pcf->Length= 8; + } // endif tc + + if (!field) + break; + + // Allocate the column description block + cdp= new(g) COLDEF; + + if ((nof= cdp->Define(g, NULL, pcf, poff)) < 0) + return -1; // Error, probably unhandled type + else if (nof) + loff= cdp->GetOffset(); + + switch (tc) { + case TAB_VEC: + cdp->SetOffset(0); // Not to have shift + case TAB_BIN: + // BIN/VEC are packed by default + if (nof) + // Field width is the internal representation width + // that can also depend on the column format + switch (cdp->Fmt ? *cdp->Fmt : 'X') { + case 'C': break; + case 'R': + case 'F': + case 'L': + case 'I': nof= 4; break; + case 'D': nof= 8; break; + case 'S': nof= 2; break; + case 'T': nof= 1; break; + default: nof= cdp->Clen; + } // endswitch Fmt + + default: + break; + } // endswitch tc + + if (lcdp) + lcdp->SetNext(cdp); + else + tocols= cdp; + + lcdp= cdp; + i++; + } // endwhile + + // Degree is the the number of defined columns (informational) + if (i != defp->GetDegree()) + defp->SetDegree(i); + + if (defp->GetDefType() == TYPE_AM_DOS) { + int ending, recln= 0; + PDOSDEF ddp= (PDOSDEF)defp; + + // Was commented because sometimes ending is 0 even when + // not specified (for instance if quoted is specified) +// if ((ending= Hc->GetIntegerOption("Ending")) < 0) { + if ((ending= Hc->GetIntegerOption("Ending")) <= 0) { +#if defined(WIN32) + ending= 2; +#else + ending= 1; +#endif + Hc->SetIntegerOption("Ending", ending); + } // endif ending + + // Calculate the default record size + switch (tc) { + case TAB_FIX: + recln= nlg + ending; // + length of line ending + break; + case TAB_BIN: + case TAB_VEC: + recln= nlg; + +// if ((k= (pak < 0) ? 8 : pak) > 1) + // See above for detailed comment + // Round up lrecl to multiple of 8 or pak +// recln= ((recln + k - 1) / k) * k; + + break; + case TAB_DOS: + case TAB_DBF: + recln= nlg; + break; + case TAB_CSV: + case TAB_FMT: + // The number of separators (assuming an extra one can exist) +// recln= poff * ((qotd) ? 3 : 1); to be investigated + recln= nlg + poff * 3; // To be safe + default: + break; + } // endswitch tc + + // lrecl must be at least recln to avoid buffer overflow + recln= max(recln, Hc->GetIntegerOption("Lrecl")); + Hc->SetIntegerOption("Lrecl", recln); + ddp->SetLrecl(recln); + } // endif Lrecl + + // Attach the column definition to the tabdef + defp->SetCols(tocols); + return poff; + } // end of GetColCatInfo + +/***********************************************************************/ +/* GetIndexInfo: retrieve index description from the table structure. */ +/***********************************************************************/ +bool MYCAT::GetIndexInfo(PGLOBAL g, PTABDEF defp) + { + // Attach new index(es) + defp->SetIndx(Hc->GetIndexInfo()); + return false; + } // end of GetIndexInfo + +/***********************************************************************/ +/* GetTableDesc: retrieve a table descriptor. */ +/* Look for a table descriptor matching the name and type. If found */ +/* in storage, return a pointer to it, else look in the XDB file. If */ +/* found, make and add the descriptor and return a pointer to it. */ +/***********************************************************************/ +PRELDEF MYCAT::GetTableDesc(PGLOBAL g, LPCSTR name, + LPCSTR type, PRELDEF *prp) + { + if (xtrace) + printf("GetTableDesc: name=%s am=%s\n", name, SVP(type)); + + // Firstly check whether this table descriptor is in memory + if (To_Desc) + return To_Desc; + + // If not specified get the type of this table + if (!type && !(type= Hc->GetStringOption("Type"))) + type= "DOS"; + + return MakeTableDesc(g, name, type); + } // end of GetTableDesc + +/***********************************************************************/ +/* MakeTableDesc: make a table/view description. */ +/* Note: caller must check if name already exists before calling it. */ +/***********************************************************************/ +PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) + { + TABTYPE tc; + PRELDEF tdp= NULL; + + if (xtrace) + printf("MakeTableDesc: name=%s am=%s\n", name, SVP(am)); + + /*********************************************************************/ + /* Get a unique enum identifier for types. */ + /*********************************************************************/ + tc= GetTypeID(am); + + switch (tc) { + case TAB_FIX: + case TAB_BIN: + case TAB_DBF: + case TAB_DOS: tdp= new(g) DOSDEF; break; + case TAB_CSV: + case TAB_FMT: tdp= new(g) CSVDEF; break; + case TAB_INI: tdp= new(g) INIDEF; break; + case TAB_DIR: tdp= new(g) DIRDEF; break; +#if defined(XML_SUPPORT) + case TAB_XML: tdp= new(g) XMLDEF; break; +#endif // XML_SUPPORT + case TAB_VEC: tdp= new(g) VCTDEF; break; +#if defined(ODBC_SUPPORT) + case TAB_ODBC: tdp= new(g) ODBCDEF; break; +#endif // ODBC_SUPPORT +#if defined(WIN32) + case TAB_MAC: tdp= new(g) MACDEF; break; + case TAB_WMI: tdp= new(g) WMIDEF; break; +#endif // WIN32 + case TAB_OEM: tdp= new(g) OEMDEF; break; + case TAB_TBL: tdp= new(g) TBLDEF; break; +#if defined(MYSQL_SUPPORT) + case TAB_MYSQL: tdp= new(g) MYSQLDEF; break; +#endif // MYSQL_SUPPORT +//#if defined(PIVOT_SUPPORT) +// case TAB_PIVOT: tdp= new(g) PIVOTDEF; break; +//#endif // PIVOT_SUPPORT + default: + sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name); + } // endswitch + + // Do make the table/view definition from XDB file information + if (tdp && tdp->Define(g, this, name, am)) + tdp= NULL; + + return tdp; + } // end of MakeTableDesc + +/***********************************************************************/ +/* Initialize a Table Description Block construction. */ +/***********************************************************************/ +PTDB MYCAT::GetTable(PGLOBAL g, PTABLE tablep, MODE mode, LPCSTR type) + { + PRELDEF tdp; + PTDB tdbp= NULL; + LPCSTR name= tablep->GetName(); + + if (xtrace) + printf("GetTableDB: name=%s\n", name); + + // Look for the description of the requested table + tdp= GetTableDesc(g, name, type); + + if (tdp) { + if (xtrace) + printf("tdb=%p type=%s\n", tdp, tdp->GetType()); + + if (tablep->GetQualifier()) + SetPath(g, &tdp->Database, tablep->GetQualifier()); + + tdbp= tdp->GetTable(g, mode); + } // endif tdp + + if (tdbp) { + if (xtrace) + printf("tdbp=%p name=%s amtype=%d\n", tdbp, tdbp->GetName(), + tdbp->GetAmType()); + tablep->SetTo_Tdb(tdbp); + tdbp->SetTable(tablep); + } // endif tdbp + + return (tdbp); + } // end of GetTable + +/***********************************************************************/ +/* ClearDB: Terminates Database usage. */ +/***********************************************************************/ +void MYCAT::ClearDB(PGLOBAL g) + { + To_Desc= NULL; + } // end of ClearDB + +/* ------------------------ End of MYCAT --------------------------- */ diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h new file mode 100644 index 00000000000..c878d5be1a8 --- /dev/null +++ b/storage/connect/mycat.h @@ -0,0 +1,81 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/**************** MYCAT H Declares Source Code File (.H) ***************/ +/* Name: MYCAT.H Version 2.3 */ +/* */ +/* This file contains the CONNECT plugin MYCAT class definitions. */ +/***********************************************************************/ +#ifndef __MYCAT__H +#define __MYCAT__H + +#include "block.h" +#include "catalog.h" + +// Possible value for catalog functions +#define FNC_NO (1 << 0) // Not a catalog table +#define FNC_COL (1 << 1) // Column catalog function +#define FNC_TABLE (1 << 2) // Table catalog function +#define FNC_DSN (1 << 3) // Data Source catalog function +#define FNC_DRIVER (1 << 4) // Column catalog function +#define FNC_NIY (1 << 5) // Catalog function NIY + +typedef class ha_connect *PHC; + +TABTYPE GetTypeID(const char *type); +bool IsFileType(TABTYPE type); +bool IsTypeNullable(TABTYPE type); +bool IsTypeFixed(TABTYPE type); +uint GetFuncID(const char *func); + +/***********************************************************************/ +/* MYCAT: class for managing the CONNECT plugin DB items. */ +/***********************************************************************/ +class MYCAT : public CATALOG { + public: + MYCAT(PHC hc); // Constructor + + // Implementation + PHC GetHandler(void) {return Hc;} + void SetHandler(PHC hc) {Hc= hc;} + + // Methods + void Reset(void); + void SetDataPath(PGLOBAL g, const char *path) + {SetPath(g, &DataPath, path);} + bool GetBoolCatInfo(PSZ what, bool bdef); + bool SetIntCatInfo(PSZ what, int ival); + int GetIntCatInfo(PSZ what, int idef); + int GetSizeCatInfo(PSZ what, PSZ sdef); + int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size); + char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef); + int GetColCatInfo(PGLOBAL g, PTABDEF defp); + bool GetIndexInfo(PGLOBAL g, PTABDEF defp); + bool StoreIndex(PGLOBAL g, PTABDEF defp) {return false;} // Temporary + PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, + LPCSTR type, PRELDEF *prp = NULL); + PTDB GetTable(PGLOBAL g, PTABLE tablep, + MODE mode = MODE_READ, LPCSTR type = NULL); + void ClearDB(PGLOBAL g); + + protected: + PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am); + void SetPath(PGLOBAL g, LPCSTR *datapath, const char *path); + + // Members + ha_connect *Hc; // The Connect handler + }; // end of class MYCAT + +#endif /* __MYCAT__H */ diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp new file mode 100644 index 00000000000..3edf07004ea --- /dev/null +++ b/storage/connect/myconn.cpp @@ -0,0 +1,768 @@ +/************** MyConn C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: MYCONN */ +/* ------------- */ +/* Version 1.7 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2007-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* Implements a connection to MySQL. */ +/* It can optionally use the embedded MySQL library. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* MYCONN.CPP - Source code */ +/* MYCONN.H - MYCONN class declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/************************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//#include <windows.h> +#else // !WIN32 +#include "osutil.h" +#endif // !WIN32 + +#include "global.h" +#include "plgdbsem.h" +#include "plgcnx.h" // For DB types +#include "resource.h" +//#include "value.h" +#include "valblk.h" +#define DLL_EXPORT // Items are exported from this DLL +#include "myconn.h" + +#if defined(EMBEDDED) +static char *server_args[] = { + "this_program", /* this string is not used */ + "--skip-bdb", + "--skip-innodb" + }; + +static char *server_groups[] = { + "PlugDB_SERVER", + "embedded", + "server", + (char *)NULL + }; +#endif // EMBEDDED + +extern "C" int trace; + +/************************************************************************/ +/* MyColumns: constructs the result blocks containing all columns */ +/* of a MySQL table that will be retrieved by GetData commands. */ +/* key = TRUE when called from Create Table to get key informations. */ +/************************************************************************/ +PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db, + const char *user, const char *pwd, + const char *table, const char *colpat, + int port, bool key, bool info) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, DB_INT, + DB_CHAR, DB_SHORT, DB_SHORT, DB_SHORT, + DB_CHAR, DB_CHAR, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, + TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, + TYPE_STRING, TYPE_STRING, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, + FLD_KEY, FLD_SCALE, FLD_RADIX, FLD_NULL, + FLD_REM, FLD_NO, FLD_CHARSET}; + static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32}; + char *fld, *fmt, cmd[128]; + int i, n, nf, ncol = sizeof(dbtype) / sizeof(int); + int len, type, prec, rc, k = 0; + PQRYRES qrp; + PCOLRES crp; + MYSQLC myc; + + if (!info) { + /********************************************************************/ + /* Open the connection with the MySQL server. */ + /********************************************************************/ + if (myc.Open(g, host, db, user, pwd, port)) + return NULL; + + /********************************************************************/ + /* Do an evaluation of the result size. */ + /********************************************************************/ + sprintf(cmd, "SHOW FULL COLUMNS FROM %s", table); + strcat(strcat(cmd, " FROM "), (db) ? db : PlgGetUser(g)->DBName); + + if (colpat) + strcat(strcat(cmd, " LIKE "), colpat); + + if (trace) + htrc("MyColumns: cmd='%s'\n", cmd); + + if ((n = myc.GetResultSize(g, cmd)) < 0) { + myc.Close(); + return NULL; + } // endif n + + /********************************************************************/ + /* Get the size of the name columns. */ + /********************************************************************/ + length[0] = myc.GetFieldLength(0); + } else { + n = 0; + length[0] = 128; + } // endif info + +//if (!key) // We are not called from Create table +// ncol--; // No date format column yet + + /**********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /**********************************************************************/ + qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, + dbtype, buftyp, fldtyp, length, true, true); + + // Some columns must be renamed + for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) + switch (++i) { + case 4: crp->Name = "Length"; break; + case 5: crp->Name = "Key"; break; + case 10: crp->Name = "Date_fmt"; break; + case 11: crp->Name = "Collation"; break; + } // endswitch i + + if (info) + return qrp; + + /**********************************************************************/ + /* Now get the results into blocks. */ + /**********************************************************************/ + for (i = 0; i < n; i++) { + if ((rc = myc.Fetch(g, -1) == RC_FX)) { + myc.Close(); + return NULL; + } else if (rc == RC_NF) + break; + + // Get column name + fld = myc.GetCharField(0); + crp = qrp->Colresp; // Column_Name + crp->Kdata->SetValue(fld, i); + + // Get type, type name, and precision + fld = myc.GetCharField(1); + prec = 0; + len = 256; // Default for text or blob + + if ((nf = sscanf(fld, "%[^(](%d,%d", cmd, &len, &prec)) < 1) { + sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld); + myc.Close(); + return NULL; + } else + qrp->Nblin++; + + if ((type = MYSQLtoPLG(cmd)) == TYPE_ERROR) { + sprintf(g->Message, "Unsupported column type %s", cmd); + myc.Close(); + return NULL; + } // endif type + + crp = crp->Next; // Data_Type + crp->Kdata->SetValue(type, i); + crp = crp->Next; // Type_Name + crp->Kdata->SetValue(cmd, i); + + if (type == TYPE_DATE) { + // When creating tables we do need info about date columns + fmt = MyDateFmt(cmd); + len = strlen(fmt); + } else + fmt = NULL; + + crp = crp->Next; // Precision + crp->Kdata->SetValue(len, i); + + crp = crp->Next; // was Length + fld = myc.GetCharField(4); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // Scale + crp->Kdata->SetValue(prec, i); + + crp = crp->Next; // Radix + crp->Kdata->SetValue(0, i); + + crp = crp->Next; // Nullable + fld = myc.GetCharField(3); + crp->Kdata->SetValue((toupper(*fld) == 'Y') ? 1 : 0, i); + + crp = crp->Next; // Remark + fld = myc.GetCharField(8); + crp->Kdata->SetValue(fld, i); + + crp = crp->Next; // New + crp->Kdata->SetValue((fmt) ? fmt : (char*) "", i); + + crp = crp->Next; // New (charset) + fld = myc.GetCharField(2); + crp->Kdata->SetValue(fld, i); + } // endfor i + + if (k > 1) { + // Multicolumn primary key + PVBLK vbp = qrp->Colresp->Next->Next->Next->Next->Kdata; + + for (i = 0; i < n; i++) + if (vbp->GetIntValue(i)) + vbp->SetValue(k, i); + + } // endif k + + /**********************************************************************/ + /* Close MySQL connection. */ + /**********************************************************************/ + myc.Close(); + + /**********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /**********************************************************************/ + return qrp; + } // end of MyColumns + +/* -------------------------- Class MYSQLC --------------------------- */ + +/***********************************************************************/ +/* Implementation of the MYSQLC class. */ +/***********************************************************************/ +MYSQLC::MYSQLC(void) + { + m_DB = NULL; + m_Stmt = NULL; + m_Res = NULL; + m_Rows = -1; + m_Row = NULL; + m_Fields = -1; + N = 0; + } // end of MYSQLC constructor + +/***********************************************************************/ +/* Get the number of lines of the result set. */ +/* Currently we send the Select command and return m_Rows */ +/* Perhaps should we use Select count(*) ... (?????) */ +/* No because here we execute only one query instead of two */ +/* (the select count(*) plus the normal query) */ +/***********************************************************************/ +int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql) + { + if (m_Rows < 0) + if (ExecSQL(g, sql) != RC_OK) + return -1; + + return m_Rows; + } // end of GetResultSize + +/***********************************************************************/ +/* Open a MySQL (remote) connection. */ +/***********************************************************************/ +int MYSQLC::Open(PGLOBAL g, const char *host, const char *db, + const char *user, const char *pwd, + int pt) + { + m_DB = mysql_init(NULL); + + if (!m_DB) { + strcpy(g->Message, "mysql_init failed: no memory"); + return RC_FX; + } // endif m_DB + + // Notice that the client and server use separate group names. + // This is critical, because the server will not accept the + // client's options, and vice versa. + mysql_options(m_DB, MYSQL_READ_DEFAULT_GROUP, "PlugDB_CLIENT"); + +#if 0 + if (pwd && !strcmp(pwd, "*")) { + if (GetPromptAnswer(g, "*Enter password:")) { + m_DB = NULL; + return RC_FX; + } else + pwd = g->Message; + + } // endif pwd +#endif // 0 + + if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, NULL, CLIENT_MULTI_RESULTS)) { +#if defined(_DEBUG) + sprintf(g->Message, "mysql_real_connect failed: (%d) %s", + mysql_errno(m_DB), mysql_error(m_DB)); +#else // !_DEBUG + sprintf(g->Message, "(%d) %s", mysql_errno(m_DB), mysql_error(m_DB)); +#endif // !_DEBUG + mysql_close(m_DB); + m_DB = NULL; + return RC_FX; + } // endif mysql_real_connect + + return RC_OK; + } // end of Open + +/***********************************************************************/ +/* Returns true if the connection is still alive. */ +/***********************************************************************/ +bool MYSQLC::Connected(void) + { +//int rc; + + if (!m_DB) + return FALSE; +//else if ((rc = mysql_ping(m_DB)) == CR_SERVER_GONE_ERROR) +// return FALSE; + else + return TRUE; + + } // end of Connected + +#if 0 // Not used +/***********************************************************************/ +/* Returns the thread ID of the current MySQL connection. */ +/***********************************************************************/ +ulong MYSQLC::GetThreadID(void) + { + return (m_DB) ? mysql_thread_id(m_DB) : 0; + } // end of GetThreadID + +/***********************************************************************/ +/* Returns a string that represents the server version number. */ +/***********************************************************************/ +const char *MYSQLC::ServerInfo(void) + { + return (m_DB) ? mysql_get_server_info(m_DB) : NULL; + } // end of ServerInfo + +/***********************************************************************/ +/* Returns the version number of the server as a number that */ +/* represents the MySQL server version in this format: */ +/* major_version*10000 + minor_version *100 + sub_version */ +/***********************************************************************/ +ulong MYSQLC::ServerVersion(void) + { + return (m_DB) ? mysql_get_server_version(m_DB) : 0; + } // end of ServerVersion +#endif // 0 + +/**************************************************************************/ +/* KillQuery: Send MySQL a Kill Query command. */ +/**************************************************************************/ +int MYSQLC::KillQuery(ulong id) + { + char kill[20]; + + sprintf(kill, "KILL QUERY %u", (unsigned int) id); +//return (m_DB) ? mysql_query(m_DB, kill) : 1; + return (m_DB) ? mysql_real_query(m_DB, kill, strlen(kill)) : 1; + } // end of KillQuery + +#if defined (MYSQL_PREPARED_STATEMENTS) +/***********************************************************************/ +/* Prepare the SQL statement used to insert into a MySQL table. */ +/***********************************************************************/ +int MYSQLC::PrepareSQL(PGLOBAL g, const char *stmt) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return -4; + } else if (m_Stmt) + return -1; // should not append + +#if defined(ALPHA) + if (!(m_Stmt = mysql_prepare(m_DB, stmt, strlen(stmt)))) { + + sprintf(g->Message, "mysql_prepare failed: %s [%s]", + mysql_error(m_DB), stmt); + return -1; + } // endif m_Stmt + + // Return the parameter count from the statement + return mysql_param_count(m_Stmt); +#else // !ALPHA + if (!(m_Stmt = mysql_stmt_init(m_DB))) { + strcpy(g->Message, "mysql_stmt_init(), out of memory"); + return -2; + } // endif m_Stmt + + if (mysql_stmt_prepare(m_Stmt, stmt, strlen(stmt))) { + sprintf(g->Message, "mysql_stmt_prepare() failed: (%d) %s", + mysql_stmt_errno(m_Stmt), mysql_stmt_error(m_Stmt)); + return -3; + } // endif prepare + + // Return the parameter count from the statement + return mysql_stmt_param_count(m_Stmt); +#endif // !ALPHA + } // end of PrepareSQL + +/***********************************************************************/ +/* Bind the parameter buffers. */ +/***********************************************************************/ +int MYSQLC::BindParams(PGLOBAL g, MYSQL_BIND *bind) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } else + assert(m_Stmt); + +#if defined(ALPHA) + if (mysql_bind_param(m_Stmt, bind)) { + sprintf(g->Message, "mysql_bind_param() failed: %s", + mysql_stmt_error(m_Stmt)); +#else // !ALPHA + if (mysql_stmt_bind_param(m_Stmt, bind)) { + sprintf(g->Message, "mysql_stmt_bind_param() failed: %s", + mysql_stmt_error(m_Stmt)); +#endif // !ALPHA + return RC_FX; + } // endif bind + + return RC_OK; + +/***********************************************************************/ +/* Execute a prepared statement. */ +/***********************************************************************/ +int MYSQLC::ExecStmt(PGLOBAL g) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + +#if defined(ALPHA) + if (mysql_execute(m_Stmt)) { + sprintf(g->Message, "mysql_execute() failed: %s", + mysql_stmt_error(m_Stmt)); + return RC_FX; + } // endif execute +#else // !ALPHA + if (mysql_stmt_execute(m_Stmt)) { + sprintf(g->Message, "mysql_stmt_execute() failed: %s", + mysql_stmt_error(m_Stmt)); + return RC_FX; + } // endif execute +#endif // !ALPHA + + // Check the total number of affected rows + if (mysql_stmt_affected_rows(m_Stmt) != 1) { + sprintf(g->Message, "Invalid affected rows by MySQL"); + return RC_FX; + } // endif affected_rows + + return RC_OK; + } // end of ExecStmt +#endif // MYSQL_PREPARED_STATEMENTS + +/***********************************************************************/ +/* Exec the Select SQL command and get back the result size in rows. */ +/***********************************************************************/ +int MYSQLC::ExecSQL(PGLOBAL g, const char *query, int *w) + { + int rc = RC_OK; + + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + + if (w) + *w = 0; + + if (m_Rows >= 0) + return RC_OK; // Already done + +//if (mysql_query(m_DB, query) != 0) { + if (mysql_real_query(m_DB, query, strlen(query))) { + char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query)); + + sprintf(msg, "(%d) %s [%s]", mysql_errno(m_DB), + mysql_error(m_DB), query); + strncpy(g->Message, msg, sizeof(g->Message) - 1); + g->Message[sizeof(g->Message) - 1] = 0; + rc = RC_FX; +//} else if (mysql_field_count(m_DB) > 0) { + } else if (m_DB->field_count > 0) { + if (!(m_Res = mysql_store_result(m_DB))) { + char *msg = (char*)PlugSubAlloc(g, NULL, 512 + strlen(query)); + + sprintf(msg, "mysql_store_result failed: %s", mysql_error(m_DB)); + strncpy(g->Message, msg, sizeof(g->Message) - 1); + g->Message[sizeof(g->Message) - 1] = 0; + rc = RC_FX; + } else { + m_Fields = mysql_num_fields(m_Res); + m_Rows = (int)mysql_num_rows(m_Res); + } // endif m_Res + + } else { +// m_Rows = (int)mysql_affected_rows(m_DB); + m_Rows = (int)m_DB->affected_rows; + sprintf(g->Message, "Affected rows: %d\n", m_Rows); + rc = RC_NF; + } // endif field count + +if (w) +//*w = mysql_warning_count(m_DB); + *w = m_DB->warning_count; + + return rc; + } // end of ExecSQL + +/***********************************************************************/ +/* Move to a specific row and column */ +/***********************************************************************/ +void MYSQLC::DataSeek(my_ulonglong row) + { + MYSQL_ROWS *tmp=0; +//DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + + if (m_Res->data) + for (tmp = m_Res->data->data; row-- && tmp; tmp = tmp->next) ; + + m_Res->current_row = 0; + m_Res->data_cursor = tmp; + } // end of DataSeek + +/***********************************************************************/ +/* Fetch one result line from the query result set. */ +/***********************************************************************/ +int MYSQLC::Fetch(PGLOBAL g, int pos) + { + if (!m_DB) { + strcpy(g->Message, "MySQL not connected"); + return RC_FX; + } // endif m_DB + + if (!m_Res) { + // Result set was not initialized + strcpy(g->Message, MSG(FETCH_NO_RES)); + return RC_FX; + } else + N++; + + if (pos >= 0) +// mysql_data_seek(m_Res, (my_ulonglong)pos); + DataSeek((my_ulonglong)pos); + + m_Row = mysql_fetch_row(m_Res); + return (m_Row) ? RC_OK : RC_EF; + } // end of Fetch + +/***********************************************************************/ +/* Get one field of the current row. */ +/***********************************************************************/ +char *MYSQLC::GetCharField(int i) + { + if (m_Res && m_Row) { +#if defined(_DEBUG) +// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i); +#endif // _DEBUG + MYSQL_ROW row = m_Row + i; + + return (row) ? (char*)*row : (char*)"<null>"; + } else + return NULL; + + } // end of GetCharField + +/***********************************************************************/ +/* Get the max length of the field. */ +/***********************************************************************/ +int MYSQLC::GetFieldLength(int i) + { + if (m_Res) { +// MYSQL_FIELD *fld = mysql_fetch_field_direct(m_Res, i); +// return fld->max_length; + return (m_Res)->fields[i].max_length; + } else + return 0; + + } // end of GetFieldLength + +/***********************************************************************/ +/* Return next field of the query results. */ +/***********************************************************************/ +MYSQL_FIELD *MYSQLC::GetNextField(void) + { + return (m_Res->current_field >= m_Res->field_count) ? NULL + : &m_Res->fields[m_Res->current_field++]; + } // end of GetNextField + +/***********************************************************************/ +/* Make a CONNECT result structure from the MySQL result. */ +/***********************************************************************/ +PQRYRES MYSQLC::GetResult(PGLOBAL g, bool pdb) + { + char *fmt; + int n; + PCOLRES *pcrp, crp; + PQRYRES qrp; + MYSQL_FIELD *fld; + MYSQL_ROW row; + + if (!m_Res || !m_Fields) { + sprintf(g->Message, "%s result", (m_Res) ? "Void" : "No"); + return NULL; + } // endif m_Res + + /*********************************************************************/ + /* Put the result in storage for future retrieval. */ + /*********************************************************************/ + qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); + pcrp = &qrp->Colresp; + qrp->Continued = FALSE; + qrp->Truncated = FALSE; + qrp->Info = FALSE; + qrp->Suball = TRUE; + qrp->BadLines = 0; + qrp->Maxsize = m_Rows; + qrp->Maxres = m_Rows; + qrp->Nbcol = 0; + qrp->Nblin = 0; + qrp->Cursor = 0; + + +//for (fld = mysql_fetch_field(m_Res); fld; +// fld = mysql_fetch_field(m_Res)) { + for (fld = GetNextField(); fld; fld = GetNextField()) { + *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); + crp = *pcrp; + pcrp = &crp->Next; + crp->Ncol = ++qrp->Nbcol; + + crp->Name = (char*)PlugSubAlloc(g, NULL, fld->name_length + 1); + strcpy(crp->Name, fld->name); + + if ((crp->Type = MYSQLtoPLG(fld->type)) == TYPE_ERROR) { + sprintf(g->Message, "Type %d not supported for column %s", + fld->type, crp->Name); + return NULL; + } else if (crp->Type == TYPE_DATE && !pdb) + // For direct MySQL connection, display the MySQL date string + crp->Type = TYPE_STRING; + + crp->Prec = fld->decimals; + crp->Length = fld->max_length; + crp->Clen = GetTypeSize(crp->Type, crp->Length); + crp->DBtype = GetDBType((int)crp->Type); + + if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows, + crp->Clen, 0, FALSE, TRUE))) { + sprintf(g->Message, MSG(INV_RESULT_TYPE), + GetFormatType(crp->Type)); + return NULL; + } else if (crp->Type == TYPE_DATE) { + fmt = MyDateFmt(fld->type); + crp->Kdata->SetFormat(g, fmt, strlen(fmt)); + } // endif's + + if (fld->flags & NOT_NULL_FLAG) + crp->Nulls = NULL; + else { + crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows); + memset(crp->Nulls, ' ', m_Rows); + } // endelse fld->flags + + } // endfor fld + + *pcrp = NULL; + assert(qrp->Nbcol == m_Fields); + + /*********************************************************************/ + /* Now fill the allocated result structure. */ + /*********************************************************************/ + for (n = 0; n < m_Rows; n++) { + if (!(m_Row = mysql_fetch_row(m_Res))) { + sprintf(g->Message, "Missing row %d from result", n + 1); + return NULL; + } // endif m_Row + + for (crp = qrp->Colresp; crp; crp = crp->Next) { + if ((row = m_Row + (crp->Ncol - 1))) { + if (*row) + crp->Kdata->SetValue((PSZ)*row, n); + else { + if (!*row && crp->Nulls) + crp->Nulls[n] = '*'; // Null value + + crp->Kdata->Reset(n); + } // endelse *row + } + + } // endfor crp + + } // endfor n + + qrp->Nblin = n; + return qrp; + } // end of GetResult + +/***********************************************************************/ +/* Free the current result. */ +/***********************************************************************/ +void MYSQLC::FreeResult(void) + { + if (m_Res) { + mysql_free_result(m_Res); + m_Res = NULL; + } // endif m_Res + + // Reset the connection + m_Row = NULL; + m_Rows = -1; + m_Fields = -1; + N = 0; + } // end of FreeResult + +/***********************************************************************/ +/* Place the cursor at the beginning of the result set. */ +/***********************************************************************/ +void MYSQLC::Rewind(void) + { + if (m_Res) + DataSeek(0); + + } // end of Rewind + +/***********************************************************************/ +/* Close the connection. */ +/***********************************************************************/ +void MYSQLC::Close(void) + { + FreeResult(); + mysql_close(m_DB); + m_DB = NULL; + } // end of Close + +#if 0 // not used yet +/***********************************************************************/ +/* Discard additional results from a stored procedure. */ +/***********************************************************************/ +void MYSQLC::DiscardResults(void) + { + MYSQL_RES *res; + + while (!mysql_next_result(m_DB)) { + res = mysql_store_result(m_DB); + mysql_free_result(res); + } // endwhile next result + + } // end of DiscardResults +#endif // 0 diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h new file mode 100644 index 00000000000..72ce58e70aa --- /dev/null +++ b/storage/connect/myconn.h @@ -0,0 +1,95 @@ +/***********************************************************************/ +/* MYCONN.H Olivier Bertrand 2007-2013 */ +/* */ +/* This is the declaration file for the MySQL connection class and */ +/* a few utility functions used to communicate with MySQL. */ +/* */ +/* DO NOT define DLL_EXPORT in your application so these items are */ +/* declared are imported from the Myconn DLL. */ +/***********************************************************************/ +#if defined(WIN32) +#include <winsock.h> +#else // !WIN32 +#include <sys/socket.h> +#endif // !WIN32 +#include <mysql.h> +#include <errmsg.h> +#include "myutil.h" + +#if defined(WIN32) && defined(MYCONN_EXPORTS) +#if defined(DLL_EXPORT) +#define DllItem _declspec(dllexport) +#else // !DLL_EXPORT +#define DllItem _declspec(dllimport) +#endif // !DLL_EXPORT +#else // !WIN32 || !MYCONN_EXPORTS +#define DllItem +#endif // !WIN32 + +//#define TYPE_AM_MYSQL (AMT)192 +#define MYSQL_ENABLED 0x00000001 +#define MYSQL_LOGON 0x00000002 + +typedef class MYSQLC *PMYC; + +/***********************************************************************/ +/* Prototypes of info functions. */ +/***********************************************************************/ +PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db, + const char *user, const char *pwd, + const char *table, const char *colpat, + int port, bool key, bool info); + +/* -------------------------- MYCONN class --------------------------- */ + +/***********************************************************************/ +/* MYSQLC exported/imported class. A MySQL connection. */ +/***********************************************************************/ +class DllItem MYSQLC { + friend class TDBMYSQL; + // Construction + public: + MYSQLC(void); + + // Implementation + int GetRows(void) {return m_Rows;} + bool Connected(void); + + // Methods +// int GetCurPos(void) {return (m_Res) ? N : 0;} +// int GetProgCur(void) {return N;} + int GetResultSize(PGLOBAL g, PSZ sql); + int Open(PGLOBAL g, const char *host, const char *db, + const char *user= "root", const char *pwd= "*", + int pt= 0); +//ulong GetThreadID(void); +//ulong ServerVersion(void); +//const char *ServerInfo(void); + int KillQuery(ulong id); + int ExecSQL(PGLOBAL g, const char *query, int *w = NULL); + int PrepareSQL(PGLOBAL g, const char *query); + int ExecStmt(PGLOBAL g); + int BindParams(PGLOBAL g, MYSQL_BIND *bind); + PQRYRES GetResult(PGLOBAL g, bool pdb = FALSE); + int Fetch(PGLOBAL g, int pos); + char *GetCharField(int i); + int GetFieldLength(int i); + void Rewind(void); + void FreeResult(void); + void Close(void); +//void DiscardResults(void); + + protected: + MYSQL_FIELD *GetNextField(void); + void DataSeek(my_ulonglong row); + + // Members + MYSQL *m_DB; // The return from MySQL connection + MYSQL_STMT *m_Stmt; // Prepared statement handle + MYSQL_RES *m_Res; // Points to MySQL Result + MYSQL_ROW m_Row; // Point to current row + int m_Rows; // The number of rows of the result + int N; + int m_Fields; // The number of result fields + }; // end of class MYSQLC + diff --git a/storage/connect/mysql-test/connect/r/bin.result b/storage/connect/mysql-test/connect/r/bin.result new file mode 100644 index 00000000000..d476aef6293 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/bin.result @@ -0,0 +1,197 @@ +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=BIN; +Warnings: +Warning 1105 No file name. Table will use t1.bin +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=BIN FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=BIN FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=BIN; +Warnings: +Warning 1105 No file name. Table will use t1.bin +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +# +# Testing errors +# +CREATE TABLE t1 +( +ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=BIN FILE_NAME='nonexistent.txt'; +SELECT * FROM t1; +ID +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/nonexistent.txt: No such file or directory +DROP TABLE t1; +SET time_zone='+00:00'; +CREATE TABLE t1 +( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) not null, +birth DATE NOT NULL, +id CHAR(5) NOT NULL FIELD_FORMAT='S', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', +dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat'; +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +INSERT INTO t1 VALUES (55555,'RONALD','1980-02-26','3333',4444.44,555); +ERROR HY000: Got error 122 'Value too long for field fig (5 --> 4)' from CONNECT +INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555); +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +DROP TABLE t1; +# +# Testing READONLY tables +# +CREATE TABLE t1 +( +fig INT(4) NOT NULL FIELD_FORMAT='C', +name CHAR(10) not null, +birth DATE NOT NULL, +id CHAR(5) NOT NULL FIELD_FORMAT='S', +salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', +dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN READONLY=Yes FILE_NAME='Testbal.dat'; +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +ALTER TABLE t1 READONLY=NO; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `fig` int(4) NOT NULL `FIELD_FORMAT`='C', + `name` char(10) NOT NULL, + `birth` date NOT NULL, + `id` char(5) NOT NULL `FIELD_FORMAT`='S', + `salary` double(9,2) NOT NULL DEFAULT '0.00' `FIELD_FORMAT`='F', + `dept` int(4) NOT NULL `FIELD_FORMAT`='S' +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=BIN `FILE_NAME`='Testbal.dat' `READONLY`=NO +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +SELECT * FROM t1; +fig name birth id salary dept +5500 ARCHIBALD 1980-01-25 3789 4380.50 318 +123 OLIVER 1953-08-10 23456 3400.68 2158 +3123 FOO 2002-07-23 888 0.00 318 +5555 RONALD 1980-02-26 3333 4444.44 555 +7777 BILL 1973-06-30 4444 5555.56 777 +ALTER TABLE t1 READONLY=YES; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `fig` int(4) NOT NULL `FIELD_FORMAT`='C', + `name` char(10) NOT NULL, + `birth` date NOT NULL, + `id` char(5) NOT NULL `FIELD_FORMAT`='S', + `salary` double(9,2) NOT NULL DEFAULT '0.00' `FIELD_FORMAT`='F', + `dept` int(4) NOT NULL `FIELD_FORMAT`='S' +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=BIN `FILE_NAME`='Testbal.dat' `READONLY`=YES +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DROP TABLE t1; +# +# Testing that the underlying file is created +# +CREATE TABLE t1 +( +c CHAR(4) NOT NULL FIELD_FORMAT='C' +) ENGINE=CONNECT TABLE_TYPE=BIN FILE_NAME='bin2.dat'; +INSERT INTO t1 VALUES (10),(20),(300),(4000); +SELECT * FROM t1; +c +10 +20 +300 +4000 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/csv.result b/storage/connect/mysql-test/connect/r/csv.result new file mode 100644 index 00000000000..34734e164c0 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/csv.result @@ -0,0 +1,319 @@ +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=CSV; +Warnings: +Warning 1105 No file name. Table will use t1.csv +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=CSV; +Warnings: +Warning 1105 No file name. Table will use t1.csv +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +SET NAMES utf8; +# +# Testing errors +# +CREATE TABLE t1 +( +ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=CSV FILE_NAME='nonexistent.txt'; +SELECT * FROM t1; +ID +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/nonexistent.txt: No such file or directory +DROP TABLE t1; +# +# Testing examples from the manual +# +CREATE TABLE t1 +( +name CHAR(12) NOT NULL, +birth DATE NOT NULL DATE_FORMAT='DD/MM/YY', +children SMALLINT(2) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='people.csv' + HEADER=1 SEP_CHAR=';' QUOTED=1; +SELECT * FROM t1; +name birth children +Archibald 2001-05-17 3 +Nabucho 2003-08-12 2 +INSERT INTO t1 VALUES ('RONALD','1980-02-26',4); +SELECT * FROM t1; +name birth children +Archibald 2001-05-17 3 +Nabucho 2003-08-12 2 +RONALD 1980-02-26 4 +DROP TABLE t1; +SELECT REPLACE(LOAD_FILE('DATADIR/test/people.csv'),'\r\n','\n');; +REPLACE(LOAD_FILE('DATADIR/test/people.csv'),'\r\n','\n') +Name;birth;children +"Archibald";17/05/01;3 +"Nabucho";12/08/03;2 +"RONALD";26/02/80;4 + +# +# Testing READONLY tables +# +CREATE TABLE t1 +( +name CHAR(12) NOT NULL, +birth DATE NOT NULL DATE_FORMAT='DD/MM/YY', +children SMALLINT(2) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='people.csv' + HEADER=1 SEP_CHAR=';' QUOTED=1 READONLY=yes; +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +UPDATE t1 SET children=6 WHERE name='BILL'; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DELETE FROM t1 WHERE name='BILL'; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +TRUNCATE TABLE t1; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +SELECT * FROM t1; +name birth children +Archibald 2001-05-17 3 +Nabucho 2003-08-12 2 +RONALD 1980-02-26 4 +ALTER TABLE t1 READONLY=no; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` char(12) NOT NULL, + `birth` date NOT NULL `DATE_FORMAT`='DD/MM/YY', + `children` smallint(2) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=CSV `FILE_NAME`='people.csv' `HEADER`=1 `SEP_CHAR`=';' `QUOTED`=1 `READONLY`=no +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +SELECT * FROM t1; +name birth children +Archibald 2001-05-17 3 +Nabucho 2003-08-12 2 +RONALD 1980-02-26 4 +BILL 1973-06-30 5 +ALTER TABLE t1 READONLY=1; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` char(12) NOT NULL, + `birth` date NOT NULL `DATE_FORMAT`='DD/MM/YY', + `children` smallint(2) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=CSV `FILE_NAME`='people.csv' `HEADER`=1 `SEP_CHAR`=';' `QUOTED`=1 `READONLY`=1 +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +SELECT * FROM t1; +name birth children +Archibald 2001-05-17 3 +Nabucho 2003-08-12 2 +RONALD 1980-02-26 4 +BILL 1973-06-30 5 +DROP TABLE t1; +# +# Testing that the underlying file is created +# +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL, +c2 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='tmp.csv' + HEADER=1 SEP_CHAR=',' QUOTED=1; +INSERT INTO t1 VALUES (10,10),(20,20),(300,300),(4000,4000), ('a b','c d'); +SELECT * FROM t1; +c1 c2 +10 10 +20 20 +300 300 +4000 4000 +a b c d +DROP TABLE t1; +SELECT REPLACE(LOAD_FILE('DATADIR/test/tmp.csv'),'\r\n','\n');; +REPLACE(LOAD_FILE('DATADIR/test/tmp.csv'),'\r\n','\n') +"c1","c2" +"10","10" +"20","20" +"300","300" +"4000","4000" +"a b","c d" + +# +# Creating a CSV table from a MyISAM table +# +CREATE TABLE t1 (a VARCHAR(10) NOT NULL, b INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('test1',1), ('test2',2); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t2.csv' + AS SELECT * FROM t1; +SELECT * FROM t2; +a b +test1 1 +test2 2 +DROP TABLE t2; +DROP TABLE t1; +SELECT REPLACE(LOAD_FILE('DATADIR/test/t2.csv'),'\r\n','\n');; +REPLACE(LOAD_FILE('DATADIR/test/t2.csv'),'\r\n','\n') +test1,1 +test2,2 + +# +# Testing international data +# +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=utf8; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +c1 +á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +C3A10A +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=utf8 DATA_CHARSET=latin1; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +c1 +á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +E10A +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv'; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +c1 +á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +E10A +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=latin1; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +c1 +á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +E10A +CREATE TABLE t1 +( +c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=latin1 DATA_CHARSET=utf8; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +c1 +á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +C3A10A +CREATE TABLE t1 +( +c1 CHAR(12) CHARACTER SET latin1 NOT NULL, +c2 CHAR(12) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv'; +INSERT INTO t1 VALUES ('á','á'); +SELECT * FROM t1; +c1 c2 +á á +DROP TABLE t1; +SELECT HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n'));; +HEX(REPLACE(LOAD_FILE('DATADIR/test/t1.csv'),'\r\n','\n')) +E12CC3A10A diff --git a/storage/connect/mysql-test/connect/r/dbf.result b/storage/connect/mysql-test/connect/r/dbf.result new file mode 100644 index 00000000000..277e9e8d0d6 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/dbf.result @@ -0,0 +1,746 @@ +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF; +Warnings: +Warning 1105 No file name. Table will use t1.dbf +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF; +Warnings: +Warning 1105 No file name. Table will use t1.dbf +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +# +# Testing errors +# +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' +SELECT * FROM t1; +a +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/t1.dbf: No such file or directory +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: Cannot open DATADIR/test/t1.dbf +SHOW WARNINGS; +Level Code Message +Error 1105 Cannot open DATADIR/test/t1.dbf +CREATE PROCEDURE test.dbf_field(in fieldno INT, in content BLOB) DETERMINISTIC +BEGIN +SELECT '---'; +SELECT fieldno AS `FieldN`; +SELECT TRIM(TRAILING 0x00 FROM LEFT(content, 10)) AS `Name`; +SELECT SUBSTRING(content, 12, 1) AS `Type`; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,13,4))),16,10) AS `Offset`; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,17,1))),16,10) AS `Length`; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,18,1))),16,10) AS `Dec`; +SELECT HEX(REVERSE(SUBSTRING(content,19,1))) AS `Flags`; +-- SELECT CONV(HEX(REVERSE(SUBSTRING(content,20,4))),16,10) AS `Next`; +-- SELECT CONV(HEX(REVERSE(SUBSTRING(content,24,4))),16,10) AS `Step`; +END// +CREATE PROCEDURE test.dbf_header(in fname VARCHAR(1024)) DETERMINISTIC +BEGIN +DECLARE content BLOB; +DECLARE offset INT; +DECLARE fieldno INT; +SELECT '--------'; +SELECT LOAD_FILE(fname) INTO content; +SELECT LENGTH(content) AS FileSize; +SELECT HEX(LEFT(content, 1)) AS DBF_Version; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,5,4))),16,10) AS NRecords; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,9,2))),16,10) AS FirstRecPos; +SELECT CONV(HEX(REVERSE(SUBSTRING(content,11,2))),16,10) AS RecLength; +SELECT HEX(REVERSE(SUBSTRING(content,29,2))) AS TableFlags; +SELECT HEX(REVERSE(SUBSTRING(content,30,1))) AS CodePageMark; +SET offset=33; +SET fieldno=0; +WHILE SUBSTR(content, offset, 1) <> 0x0D AND offset + 32 < LENGTH(content) DO +CALL dbf_field(fieldno, SUBSTRING(content, offset, 32)); +SET offset=offset + 32; +SET fieldno=fieldno + 1; +END WHILE; +SELECT '--------'; +END// +# +# Testing READONLY tables +# +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' +INSERT INTO t1 VALUES (10),(20); +SELECT * FROM t1; +a +10 +20 +ALTER TABLE t1 READONLY=Yes; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' `READONLY`=Yes +INSERT INTO t1 VALUES (30); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +UPDATE t1 SET a=30 WHERE a=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DELETE FROM t1 WHERE a=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +TRUNCATE TABLE t1; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +ALTER TABLE t1 READONLY=NO; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' `READONLY`=NO +INSERT INTO t1 VALUES (30); +SELECT * FROM t1; +a +10 +20 +30 +DROP TABLE t1; +# +# This SQL script crashed (dbf01.sql) +# +CREATE TABLE t1 +( +a int(11) NOT NULL, +b char(10) NOT NULL, +c varchar(10) NOT NULL +) ENGINE=CONNECT table_type=DBF file_name='t1.dbf'; +INSERT INTO t1 VALUES (1,'1','1'); +INSERT INTO t1 VALUES (2,'2','2'); +SELECT * FROM t1; +a b c +1 1 1 +2 2 2 +DROP TABLE t1; +# +# Testing that table options in lower case and mixed case are understood: +# +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT table_type=dbf file_name='t1.dbf'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `table_type`=dbf `file_name`='t1.dbf' +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +DROP TABLE t1; +CREATE TABLE t1 (a CHAR(10) NOT NULL) ENGINE=CONNECT Table_Type=dbf File_Name='t1.dbf'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `Table_Type`=dbf `File_Name`='t1.dbf' +INSERT INTO t1 VALUES ('test'); +SELECT * FROM t1; +a +test +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 77 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 11 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing multiple columns +# +CREATE TABLE t1 +( +a INT NOT NULL, +b CHAR(10) NOT NULL, +c VARCHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (1,'1','1'); +INSERT INTO t1 VALUES (2,'2','2'); +SELECT * FROM t1; +a b c +1 1 1 +2 2 2 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 194 +DBF_Version 03 +NRecords 2 +FirstRecPos 130 +RecLength 32 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type N +Offset 0 +Length 11 +Dec 0 +Flags 00 +--- --- +FieldN 1 +Name b +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +--- --- +FieldN 2 +Name c +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing long column name +# +CREATE TABLE t1 +( +a012345678901234567890123456789 INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: DBF: Column name 'a012345678901234567890123456789' is too long (max=10) +# +# Testing 2 columns with long names (12) +# +CREATE TABLE t1 +( +a0123456789a INT NOT NULL, +b0123456789b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x11.dbf'; +ERROR HY000: DBF: Column name 'a0123456789a' is too long (max=10) +# +# Testing 2 columns with long names (11) +# +CREATE TABLE t1 +( +a012345678a INT NOT NULL, +b012345678b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x12.dbf'; +ERROR HY000: DBF: Column name 'a012345678a' is too long (max=10) +# +# Testing 2 columns name length 10 (maximum possible length) +# +CREATE TABLE t1 +( +a01234567a INT NOT NULL, +b01234567b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x13.dbf'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a01234567a` int(11) NOT NULL, + `b01234567b` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t02x13.dbf' +INSERT INTO t1 VALUES (1,2); +SELECT * FROM t1; +a01234567a b01234567b +1 2 +DROP TABLE t1; +# +# Testing BIGINT +# +CREATE TABLE t1 +( +a bigint NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (0x7FFFFFFFFFFFFFFF); +INSERT INTO t1 VALUES (-0x8000000000000000); +SELECT * FROM t1; +a +9223372036854775807 +-9223372036854775808 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 108 +DBF_Version 03 +NRecords 2 +FirstRecPos 66 +RecLength 21 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type N +Offset 0 +Length 20 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing TINYINT +# +CREATE TABLE t1 +( +a TINYINT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +SELECT * FROM t1; +a +123 +DROP TABLE t1; +# +# Testing SMALLINT +# +CREATE TABLE t1 +( +a SMALLINT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (0x7FFF); +INSERT INTO t1 VALUES (-0x8000); +SELECT * FROM t1; +a +32767 +-32768 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 80 +DBF_Version 03 +NRecords 2 +FirstRecPos 66 +RecLength 7 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type N +Offset 0 +Length 6 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing VARCHAR +# +CREATE TABLE t1 +( +a VARCHAR(255) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (REPEAT('a',255)); +SELECT LENGTH(a) FROM t1; +LENGTH(a) +255 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 322 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 256 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type C +Offset 0 +Length 255 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing too long CHAR +# All columns longer than 255 bytes should be rejected +# +CREATE TABLE t1 +( +a CHAR(86) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: DBF: Column length too big for 'a' (max=255) +# +# Testing too long VARCHAR +# All columns longer than 255 bytes should be rejected +# +CREATE TABLE t1 +( +a VARCHAR(256) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: DBF: Column length too big for 'a' (max=255) +CREATE TABLE t1 +( +a VARCHAR(86) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: DBF: Column length too big for 'a' (max=255) +CREATE TABLE t1 +( +a VARCHAR(64000) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: DBF: Column length too big for 'a' (max=255) +# +# Testing BLOB +# +CREATE TABLE t1 +( +a BLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: Unsupported type for column 'a' +CREATE TABLE t1 +( +a TINYBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: Unsupported type for column 'a' +CREATE TABLE t1 +( +a MEDIUMBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: Unsupported type for column 'a' +CREATE TABLE t1 +( +a LONGBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +ERROR HY000: Unsupported type for column 'a' +# +# Testing DATE +# +CREATE TABLE t1 +( +a DATE NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('2001-01-01'); +SELECT * FROM t1; +a +2001-01-01 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 75 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 9 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type D +Offset 0 +Length 8 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing FLOAT +# +CREATE TABLE t1 +( +a FLOAT(12,4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +SELECT * FROM t1; +a +123.0000 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 79 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 13 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type F +Offset 0 +Length 12 +Dec 4 +Flags 00 +-------- -------- +DROP TABLE t1; +# +# Testing double +# +CREATE TABLE t1 +( +a DOUBLE(20,5) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +INSERT INTO t1 VALUES (123456789.12345); +SELECT * FROM t1; +a +123.00000 +123456789.12345 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 108 +DBF_Version 03 +NRecords 2 +FirstRecPos 66 +RecLength 21 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type F +Offset 0 +Length 20 +Dec 5 +Flags 00 +-------- -------- +DROP TABLE IF EXISTS t1; +# +# Testing ALTER +# +CREATE TABLE t1 +( +a VARCHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('10'); +SELECT * FROM t1; +a +10 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 77 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 11 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +-------- -------- +ALTER TABLE t1 MODIFY a VARCHAR(10) NOT NULL; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' +SELECT * FROM t1; +a +10 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 77 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 11 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +-------- -------- +ALTER TABLE t1 MODIFY a INT(10) NOT NULL; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DBF `FILE_NAME`='t1.dbf' +SELECT * FROM t1; +a +10 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 77 +DBF_Version 03 +NRecords 1 +FirstRecPos 66 +RecLength 11 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name a +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE IF EXISTS t1; +# +# Testing NULL +# +CREATE TABLE t1 +( +c1 VARCHAR(10) NOT NULL, +c2 VARCHAR(10) NOT NULL DEFAULT 'def', +i1 INT NOT NULL, +i2 INT NOT NULL DEFAULT 123 +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('10','10',10,10); +INSERT INTO t1(c1,i1) VALUES ('20',20); +INSERT INTO t1 VALUES ('30',DEFAULT,30,DEFAULT); +SELECT * FROM t1; +c1 c2 i1 i2 +10 10 10 10 +20 def 20 123 +30 def 30 123 +CALL dbf_header('MYSQLD_DATADIR/test/t1.dbf'); +-------- -------- +FileSize 291 +DBF_Version 03 +NRecords 3 +FirstRecPos 162 +RecLength 43 +TableFlags 0000 +CodePageMark 00 +--- --- +FieldN 0 +Name c1 +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +--- --- +FieldN 1 +Name c2 +Type C +Offset 0 +Length 10 +Dec 0 +Flags 00 +--- --- +FieldN 2 +Name i1 +Type N +Offset 0 +Length 11 +Dec 0 +Flags 00 +--- --- +FieldN 3 +Name i2 +Type N +Offset 0 +Length 11 +Dec 0 +Flags 00 +-------- -------- +DROP TABLE IF EXISTS t1; +DROP PROCEDURE test.dbf_field; +DROP PROCEDURE test.dbf_header; diff --git a/storage/connect/mysql-test/connect/r/dir.result b/storage/connect/mysql-test/connect/r/dir.result new file mode 100644 index 00000000000..0e6d109ae38 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/dir.result @@ -0,0 +1,103 @@ +# +# Testing FILE privilege +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 ( +path VARCHAR(256) NOT NULL flag=1, +fname VARCHAR(256) NOT NULL, +ftype CHAR(4) NOT NULL, +size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.*'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 ( +path VARCHAR(256) NOT NULL flag=1, +fname VARCHAR(256) NOT NULL, +ftype CHAR(4) NOT NULL, +size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.*'; +SELECT fname, ftype, size FROM t1 WHERE size>0; +fname ftype size +t1 .frm 8654 +SELECT user(); +user() +user@localhost +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO t1 VALUES ('xxx'); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1 WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a='yyy' WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +DROP USER user@localhost; +# +# Testing FILE privileges done +# +CREATE TABLE t1 ( +path VARCHAR(256) NOT NULL flag=1, +fname VARCHAR(256) NOT NULL, +ftype CHAR(4) NOT NULL, +size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.txt' + OPTION_LIST='subdir=1'; +SELECT * FROM t1; +path fname ftype size +SELECT fname, ftype, size FROM t1 ORDER BY fname, ftype, size; +fname ftype size +boys .txt 282 +boys2 .txt 282 +boyswin .txt 288 +ALTER TABLE t1 OPTION_LIST='subdir=0'; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `path` varchar(256) NOT NULL `flag`=1, + `fname` varchar(256) NOT NULL, + `ftype` char(4) NOT NULL, + `size` double(12,0) NOT NULL `flag`=5 +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=DIR `FILE_NAME`='*.txt' `OPTION_LIST`='subdir=0' +SELECT fname, ftype, size FROM t1 ORDER BY fname, ftype, size; +fname ftype size +boys .txt 282 +boyswin .txt 288 +INSERT INTO t1 VALUES ('','','',''); +ERROR HY000: Got error 174 'COLBLK SetBuffer: undefined Access Method' from CONNECT +DROP TABLE t1; +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.txt'; +ERROR HY000: Cannot get column info for table type DIR diff --git a/storage/connect/mysql-test/connect/r/fix.result b/storage/connect/mysql-test/connect/r/fix.result new file mode 100644 index 00000000000..e9eb31d9bc8 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/fix.result @@ -0,0 +1,229 @@ +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX; +Warnings: +Warning 1105 No file name. Table will use t1.fix +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX; +Warnings: +Warning 1105 No file name. Table will use t1.fix +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +# +# Testing errors +# +CREATE TABLE t1 +( +ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=DOS FILE_NAME='nonexistent.txt'; +SELECT * FROM t1; +ID +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/nonexistent.txt: No such file or directory +DROP TABLE t1; +# +# Testing READONLY tables +# +CREATE TABLE t1 +( +id INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='t1.txt'; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +id +10 +ALTER TABLE t1 READONLY=1; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=FIX `FILE_NAME`='t1.txt' `READONLY`=1 +INSERT INTO t1 VALUES (20); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +UPDATE t1 SET id=20 WHERE id=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DELETE FROM t1 WHERE id=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +TRUNCATE TABLE t1; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +ALTER TABLE t1 READONLY=0; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=FIX `FILE_NAME`='t1.txt' `READONLY`=0 +INSERT INTO t1 VALUES (20); +SELECT * FROM t1; +id +10 +20 +DROP TABLE t1; +# +# Testing manual examples +# +CREATE TABLE t1 +( +number CHAR(4) not null, +location CHAR(15) NOT NULL flag=5, +director CHAR(5) NOT NULL flag=20, +function CHAR(12) NOT NULL flag=26, +name CHAR(22) NOT NULL flag=38 +) ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='dept.dat'; +SELECT * FROM t1; +number location director function name +0318 KINGSTON 70012 SALES Bank/Insurance +0021 ARMONK 87777 CHQ Corporate headquarter +0319 HARRISON 40567 SALES Federal Administration +2452 POUGHKEEPSIE 31416 DEVELOPMENT Research & development +DROP TABLE t1; +CREATE TABLE t1 +( +name char(12) not null, +city char(12) not null, +birth date not null date_format='DD/MM/YYYY', +hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' ENDING=1; +SELECT * FROM t1; +name city birth hired +John Boston 2 1986-01-05 2010-06-02 +Henry Boston 0 1987-06-07 2008-04-01 +George San Jose 1 1981-08-01 2010-06-02 +Sam Chicago 2 1979-11-02 2007-10-10 +James Dallas 1 1992-05-03 2009-12-14 +Bill Boston 1 1986-09-01 2008-02-10 +DROP TABLE t1; +CREATE TABLE t1 +( +name char(12) not null, +city char(12) not null, +birth date not null date_format='DD/MM/YYYY', +hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' LRECL=47 ENDING=1; +SELECT * FROM t1; +name city birth hired +John Boston 2 1986-01-05 2010-06-02 +Henry Boston 0 1987-06-07 2008-04-01 +George San Jose 1 1981-08-01 2010-06-02 +Sam Chicago 2 1979-11-02 2007-10-10 +James Dallas 1 1992-05-03 2009-12-14 +Bill Boston 1 1986-09-01 2008-02-10 +DROP TABLE t1; +CREATE TABLE t1 +( +name char(12) not null, +city char(12) not null, +birth date not null date_format='DD/MM/YYYY', +hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boyswin.txt' ENDING=2; +SELECT * FROM t1; +name city birth hired +John Boston 2 1986-01-05 2010-06-02 +Henry Boston 0 1987-06-07 2008-04-01 +George San Jose 1 1981-08-01 2010-06-02 +Sam Chicago 2 1979-11-02 2007-10-10 +James Dallas 1 1992-05-03 2009-12-14 +Bill Boston 1 1986-09-01 2008-02-10 +DROP TABLE t1; +CREATE TABLE t1 +( +name char(12) not null, +city char(12) not null, +birth date not null date_format='DD/MM/YYYY', +hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boyswin.txt' LRECL=47 ENDING=2; +SELECT * FROM t1; +name city birth hired +John Boston 2 1986-01-05 2010-06-02 +Henry Boston 0 1987-06-07 2008-04-01 +George San Jose 1 1981-08-01 2010-06-02 +Sam Chicago 2 1979-11-02 2007-10-10 +James Dallas 1 1992-05-03 2009-12-14 +Bill Boston 1 1986-09-01 2008-02-10 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/fmt.result b/storage/connect/mysql-test/connect/r/fmt.result new file mode 100644 index 00000000000..dc3b42b2231 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/fmt.result @@ -0,0 +1,68 @@ +# +# Testing errors +# +CREATE TABLE t1 +( +ID INT NOT NULL field_format=' %n%d%n' +) Engine=CONNECT table_type=FMT file_name='nonexistent.txt'; +SELECT * FROM t1; +ID +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/nonexistent.txt: No such file or directory +DROP TABLE t1; +# +# Testing update on FMT tables +# +CREATE TABLE t1 +( +id INT NOT NULL field_format=' %n%d%n' +) ENGINE=CONNECT TABLE_TYPE=FMT FILE_NAME='t1.txt'; +INSERT INTO t1 VALUES (10),(20); +ERROR HY000: Got error 122 'Writing FMT files is not implemented yet' from CONNECT +DROP TABLE t1; +# +# Testing manual examples +# +CREATE TABLE t1 +( +ID Integer(5) not null field_format=' %n%d%n', +NAME Char(16) not null field_format=" , '%n%[^']%n'", +DEPNO Integer(4) not null field_format=' , #%n%d%n', +SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny.txt'; +SELECT * FROM t1; +ID NAME DEPNO SALARY +12345 BERTRAND 200 5009.13 +56 POIROT-DELMOTTE 4256 18009.00 +345 TRUCMUCHE 67 19000.25 +DROP TABLE t1; +CREATE TABLE t1 +( +ID Integer(5) not null field_format=' %n%d%n', +NAME Char(16) not null field_format=" , '%n%[^']%n'", +DEPNO Integer(4) not null field_format=' , #%n%d%n', +SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny2.txt'; +SELECT * FROM t1; +ERROR HY000: Got error 122 'Bad format line 2 field 3 of t1' from CONNECT +DROP TABLE t1; +CREATE TABLE t1 +( +ID Integer(5) not null field_format=' %n%d%n', +NAME Char(16) not null field_format=' , ''%n%[^'']%m', +DEPNO Integer(4) not null field_format=''' , #%n%d%m', +SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny2.txt'; +SELECT * FROM t1; +ID NAME DEPNO SALARY +12345 BERTRAND 200 5009.13 +56 POIROT-DELMOTTE 0 18009.00 +345 67 19000.25 +UPDATE t1 SET SALARY=1234; +ERROR HY000: Got error 122 'Writing FMT files is not implemented yet' from CONNECT +DELETE FROM t1 WHERE ID=56; +SELECT * FROM t1; +ID NAME DEPNO SALARY +12345 BERTRAND 200 5009.13 +345 67 19000.25 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/general.result b/storage/connect/mysql-test/connect/r/general.result new file mode 100644 index 00000000000..ed2c903145d --- /dev/null +++ b/storage/connect/mysql-test/connect/r/general.result @@ -0,0 +1,18 @@ +# +# Testing features not specific to any TABLE_TYPE +# +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=NON_EXISTING; +ERROR HY000: Unsupported table type NON_EXISTING +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX; +Warnings: +Warning 1105 No file name. Table will use t1.fix +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +ALTER TABLE t1 TABLE_TYPE=NON_EXISTING; +ERROR HY000: Unsupported table type NON_EXISTING +SELECT * FROM t1; +a +10 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/index.result b/storage/connect/mysql-test/connect/r/index.result new file mode 100644 index 00000000000..b365046a55d --- /dev/null +++ b/storage/connect/mysql-test/connect/r/index.result @@ -0,0 +1,143 @@ +# +# Testing indexing +# +CREATE TABLE t1 +( +matricule INT(4) KEY NOT NULL field_format='Z', +nom VARCHAR(16) NOT NULL, +prenom VARCHAR(20) NOT NULL, +sexe SMALLINT(1) NOT NULL COMMENT 'sexe 1:M 2:F', +aanais INT(4) NOT NULL, +mmnais INT(2) NOT NULL, +ddentree DATE NOT NULL date_format='YYYYMM', +ddnom DATE NOT NULL date_format='YYYYMM', +brut INT(5) NOT NULL, +net DOUBLE(8,2) NOT NULL, +service INT(2) NOT NULL, +sitmat CHAR(1) NOT NULL, +formation CHAR(5) NOT NULL, +INDEX NP(nom,prenom) +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='emp.txt' ENDING=2; +SELECT * FROM t1 LIMIT 10; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +5745 ESCOURCHE BENEDICTE 2 1935 7 1962-12-01 1994-05-01 18345 14275.50 0 M TECHN +9692 VICENTE LAURENCE 2 1941 8 1967-10-01 1989-01-01 16212 13032.80 0 M ANGL +9146 NICOLAS ROGER 1 1941 6 1964-07-01 1995-02-01 34173 25098.65 0 M SANS +2985 TESSEREAU MARIE HELENE 2 1941 9 1967-01-01 1990-01-01 19323 14933.78 0 V SANS +3368 MOGADOR ALAIN 1 1941 1 1961-09-01 1993-11-01 43303 31420.55 0 C SANS +7394 CHAUSSEE ERIC DENIS 1 1944 9 1965-11-01 1983-12-01 32002 23583.86 0 M ANGL +4655 MAILLOT GEORGES 1 1945 5 1970-09-01 1986-12-01 24700 18541.64 0 C ANGL +2825 CAMILLE NADINE 2 1956 9 1994-01-01 1993-01-01 19494 15050.45 0 M SANS +1460 BRUYERES JEAN MARC 1 1958 8 1984-08-01 1988-05-01 20902 15980.07 0 M SANS +4974 LONES GERARD 1 1959 10 1979-01-01 1994-12-01 16081 12916.70 0 M SANS +SELECT SUM(brut) from t1; +SUM(brut) +64319029 +# +# Testing file mapping +# +ALTER TABLE t1 MAPPED=yes; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SELECT * FROM t1 LIMIT 10; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +5745 ESCOURCHE BENEDICTE 2 1935 7 1962-12-01 1994-05-01 18345 14275.50 0 M TECHN +9692 VICENTE LAURENCE 2 1941 8 1967-10-01 1989-01-01 16212 13032.80 0 M ANGL +9146 NICOLAS ROGER 1 1941 6 1964-07-01 1995-02-01 34173 25098.65 0 M SANS +2985 TESSEREAU MARIE HELENE 2 1941 9 1967-01-01 1990-01-01 19323 14933.78 0 V SANS +3368 MOGADOR ALAIN 1 1941 1 1961-09-01 1993-11-01 43303 31420.55 0 C SANS +7394 CHAUSSEE ERIC DENIS 1 1944 9 1965-11-01 1983-12-01 32002 23583.86 0 M ANGL +4655 MAILLOT GEORGES 1 1945 5 1970-09-01 1986-12-01 24700 18541.64 0 C ANGL +2825 CAMILLE NADINE 2 1956 9 1994-01-01 1993-01-01 19494 15050.45 0 M SANS +1460 BRUYERES JEAN MARC 1 1958 8 1984-08-01 1988-05-01 20902 15980.07 0 M SANS +4974 LONES GERARD 1 1959 10 1979-01-01 1994-12-01 16081 12916.70 0 M SANS +SELECT SUM(brut) FROM t1; +SUM(brut) +64319029 +# +# Test the indexes (made when creating the table) +# +SELECT * FROM t1 WHERE matricule = '0091'; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +91 THIVERNAL DIDIER JEAN 1 1951 10 1980-05-01 1991-10-01 14715 12024.71 1 M SANS +SELECT * FROM t1 WHERE nom = 'FOCH'; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +1977 FOCH BERNADETTE 2 1958 3 1992-02-01 1991-02-01 8656 8145.03 1 . SANS +5707 FOCH DENIS 1 1977 7 1996-07-01 1995-07-01 7803 7679.36 15 C COMPT +2552 FOCH FRANCK 1 1962 12 1986-06-01 1990-11-01 12882 10745.81 13 M SANS +2634 FOCH JOCELYNE 2 1953 3 1996-01-01 1995-01-01 12499 10473.09 41 M INFOR +5765 FOCH ROBERT 1 1957 1 1981-03-01 1993-03-01 16081 12916.32 52 M ALLEM +4080 FOCH SERGE 1 1959 3 1981-03-01 1981-05-01 11131 9658.24 5 M SANS +SELECT * FROM t1 WHERE nom = 'FOCH' and prenom = 'DENIS'; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +5707 FOCH DENIS 1 1977 7 1996-07-01 1995-07-01 7803 7679.36 15 C COMPT +# +# Testing UPDATE +# +UPDATE t1 SET aanais = aanais + 16; +UPDATE t1 SET ddentree = adddate(ddentree, interval 16 year); +UPDATE t1 SET ddnom = adddate(ddnom, interval 16 year); +SELECT * FROM t1 WHERE nom = 'FOCH'; +matricule nom prenom sexe aanais mmnais ddentree ddnom brut net service sitmat formation +1977 FOCH BERNADETTE 2 1974 3 2008-02-01 2007-02-01 8656 8145.03 1 . SANS +5707 FOCH DENIS 1 1993 7 2012-07-01 2011-07-01 7803 7679.36 15 C COMPT +2552 FOCH FRANCK 1 1978 12 2002-06-01 2006-11-01 12882 10745.81 13 M SANS +2634 FOCH JOCELYNE 2 1969 3 2012-01-01 2011-01-01 12499 10473.09 41 M INFOR +5765 FOCH ROBERT 1 1973 1 1997-03-01 2009-03-01 16081 12916.32 52 M ALLEM +4080 FOCH SERGE 1 1975 3 1997-03-01 1997-05-01 11131 9658.24 5 M SANS +# +# Testing JOIN +# +create table t2 +( +sexe INT(1) KEY, +genre CHAR(8) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='sexe.csv' SEP_CHAR=';' ENDING=2; +SELECT * FROM t2; +sexe genre +0 Inconnu +1 Masculin +2 Feminin +SELECT nom, prenom, genre FROM t1 NATURAL JOIN t2 LIMIT 10; +nom prenom genre +ESCOURCHE BENEDICTE Feminin +VICENTE LAURENCE Feminin +NICOLAS ROGER Masculin +TESSEREAU MARIE HELENE Feminin +MOGADOR ALAIN Masculin +CHAUSSEE ERIC DENIS Masculin +MAILLOT GEORGES Masculin +CAMILLE NADINE Feminin +BRUYERES JEAN MARC Masculin +LONES GERARD Masculin +# +# Another table +# +CREATE TABLE t3 ( +sitmat CHAR(1) KEY, +situation CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='sitmat.csv' SEP_CHAR=';' ENDING=2; +SELECT * FROM t3; +sitmat situation +. Inconnu +C Celibataire +D Divorce +L Union libre +M Marie +S Separe +V Veuf +SELECT nom, prenom, genre, situation FROM t1 NATURAL JOIN t2 NATURAL JOIN t3 WHERE nom = 'FOCH'; +nom prenom genre situation +FOCH BERNADETTE Feminin Inconnu +FOCH DENIS Masculin Celibataire +FOCH FRANCK Masculin Marie +FOCH JOCELYNE Feminin Marie +FOCH ROBERT Masculin Marie +FOCH SERGE Masculin Marie +# +# Testing DELETE +# +DELETE FROM t1; +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; diff --git a/storage/connect/mysql-test/connect/r/ini.result b/storage/connect/mysql-test/connect/r/ini.result new file mode 100644 index 00000000000..da846d466ba --- /dev/null +++ b/storage/connect/mysql-test/connect/r/ini.result @@ -0,0 +1,362 @@ +# +# Checking FILE privileges +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t1.ini +INSERT INTO t1 VALUES ('sec1','val1'); +SELECT * FROM t1; +sec val +sec1 val1 +UPDATE t1 SET val='val11'; +SELECT * FROM t1; +sec val +sec1 val11 +DELETE FROM t1; +SELECT * FROM t1; +sec val +INSERT INTO t1 VALUES('sec2','val2'); +TRUNCATE TABLE t1; +SELECT * FROM t1; +sec val +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +sec val +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES ('sec1','val1'); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES ('sec2','val2'); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET val='val11'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES ('sec3','val3'); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET val='val11'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP VIEW v1; +DROP TABLE t1; +DROP USER user@localhost; +# +# Checking FILE privileges: done +# +# +# Testing errors +# +CREATE TABLE t1 +( +ID INT +) Engine=CONNECT TABLE_TYPE=INI FILE_NAME='nonexistent.txt'; +SELECT * FROM t1; +ID +DROP TABLE t1; +# +# Testing examples from the manual +# +CREATE TABLE t1 +( +contact CHAR(16) flag=1, +name CHAR(20), +forename CHAR(32), +hired date date_format='DD/MM/YYYY', +address CHAR(64), +city CHAR(20), +zipcode CHAR(8), +tel CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='contact.ini'; +SELECT contact, name, hired, city, tel FROM t1; +contact name hired city tel +BER Bertrand NULL Issy-les-Mlx 09.54.36.29.60 +WEL Schmitt 1985-02-19 Berlin 03.43.377.360 +UK1 Smith 2003-11-08 London NULL +UPDATE t1 SET forename= 'Harry' where contact='UK1'; +SELECT * FROM t1 WHERE contact='UK1'; +contact name forename hired address city zipcode tel +UK1 Smith Harry 2003-11-08 143 Blum Rd. London NW1 2BP NULL +INSERT INTO t1 (contact,forename) VALUES ('UK1','Harrison'); +SELECT * FROM t1 WHERE contact='UK1'; +contact name forename hired address city zipcode tel +UK1 Smith Harrison 2003-11-08 143 Blum Rd. London NW1 2BP NULL +INSERT INTO t1 (contact,forename) VALUES ('UK2','John'); +SELECT * FROM t1 WHERE contact='UK2'; +contact name forename hired address city zipcode tel +UK2 NULL John NULL NULL NULL NULL NULL +DROP TABLE t1; +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n') +[BER] +name=Bertrand +forename=Olivier +address=21 rue Ferdinand Buisson +city=Issy-les-Mlx +zipcode=92130 +tel=09.54.36.29.60 +cell=06.70.06.04.16 +[WEL] +name=Schmitt +forename=Bernard +hired=19/02/1985 +address=64 tiergarten strasse +city=Berlin +zipcode=95013 +tel=03.43.377.360 +[UK1] +name=Smith +forename=Harrison +hired=08/11/2003 +address=143 Blum Rd. +city=London +zipcode=NW1 2BP +[UK2] +forename=John + +CREATE TABLE t1 +( +section CHAR(16) flag=1, +keyname CHAR(16) flag=2, +value CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='contact.ini' + OPTION_LIST='Layout=Row'; +UPDATE t1 SET value='Paul' WHERE section='UK2' AND keyname='forename'; +SELECT * FROM t1; +section keyname value +BER name Bertrand +BER forename Olivier +BER address 21 rue Ferdinand Buisson +BER city Issy-les-Mlx +BER zipcode 92130 +BER tel 09.54.36.29.60 +BER cell 06.70.06.04.16 +WEL name Schmitt +WEL forename Bernard +WEL hired 19/02/1985 +WEL address 64 tiergarten strasse +WEL city Berlin +WEL zipcode 95013 +WEL tel 03.43.377.360 +UK1 name Smith +UK1 forename Harrison +UK1 hired 08/11/2003 +UK1 address 143 Blum Rd. +UK1 city London +UK1 zipcode NW1 2BP +UK2 forename Paul +DROP TABLE t1; +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n') +[BER] +name=Bertrand +forename=Olivier +address=21 rue Ferdinand Buisson +city=Issy-les-Mlx +zipcode=92130 +tel=09.54.36.29.60 +cell=06.70.06.04.16 +[WEL] +name=Schmitt +forename=Bernard +hired=19/02/1985 +address=64 tiergarten strasse +city=Berlin +zipcode=95013 +tel=03.43.377.360 +[UK1] +name=Smith +forename=Harrison +hired=08/11/2003 +address=143 Blum Rd. +city=London +zipcode=NW1 2BP +[UK2] +forename=Paul + +# +# Testing that the underlying file is created +# +CREATE TABLE t1 +( +contact CHAR(12) NOT NULL flag=1, +c2 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='tmp.ini'; +INSERT INTO t1 VALUES (10,10),(20,20),(300,300),(4000,4000), ('a b','c d'); +SELECT * FROM t1; +contact c2 +10 10 +20 20 +300 300 +4000 4000 +a b c d +DROP TABLE t1; +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/tmp.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/tmp.ini'),'\r\n','\n'),'\n\n','\n') +[10] +c2=10 +[20] +c2=20 +[300] +c2=300 +[4000] +c2=4000 +[a b] +c2=c d + +# +# Testing bad table +# +CREATE TABLE t1 +( +id INT +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.ini'; +INSERT INTO t1 VALUES (10); +ERROR HY000: Got error 122 'Section name must come first on Insert' from CONNECT +SELECT * FROM t1; +id +DROP TABLE t1; +# +# Testing READONLY tables +# +CREATE TABLE t1 +( +contact CHAR(10) flag=1, +c2 CHAR(60) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.ini'; +INSERT INTO t1 VALUES ('UK',10),('FR',20),('RU',30); +SELECT * FROM t1; +contact c2 +UK 10 +FR 20 +RU 30 +ALTER TABLE t1 READONLY=1; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `contact` char(10) DEFAULT NULL `flag`=1, + `c2` char(60) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=INI `FILE_NAME`='t1.ini' `READONLY`=1 +INSERT INTO t1 VALUES ('US',40); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +UPDATE t1 SET c2=20 WHERE c2=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DELETE FROM t1 WHERE c2=10; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +TRUNCATE TABLE t1; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +ALTER TABLE t1 READONLY=0; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `contact` char(10) DEFAULT NULL `flag`=1, + `c2` char(60) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=INI `FILE_NAME`='t1.ini' `READONLY`=0 +INSERT INTO t1 VALUES ('US',40); +SELECT * FROM t1; +contact c2 +UK 10 +FR 20 +RU 30 +US 40 +DROP TABLE t1; +# +# Bug: TABLE_TYPE=ini does not clear memory between CREATE TABLEs +# +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t1.ini +INSERT INTO t1 VALUES ('sec1','val1'),('sec2','val2'); +SELECT sec AS s, val AS v FROM t1; +s v +sec1 val1 +sec2 val2 +DROP TABLE t1; +CREATE TABLE t1 (sec2 CHAR(10) NOT NULL FLAG=1, val2 CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t1.ini +INSERT INTO t1 VALUES ('sec1','val11'),('sec2','val22'); +SELECT sec2 AS s, val2 AS v FROM t1; +s v +sec1 val11 +sec2 val22 +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n') +[sec1] +val2=val11 +[sec2] +val2=val22 + +DROP TABLE t1; +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t1.ini +CREATE TABLE t2 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t2.ini +INSERT INTO t1 VALUES('1sec1','1val1'),('1sec2','1val2'); +INSERT INTO t2 VALUES('2sec1','2val1'),('2sec2','2val2'); +SELECT sec AS s, val AS v FROM t1; +s v +1sec1 1val1 +1sec2 1val2 +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n') +[1sec1] +val=1val1 +[1sec2] +val=1val2 + +SELECT sec AS s, val AS v FROM t2; +s v +2sec1 2val1 +2sec2 2val2 +SELECT REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t2.ini'),'\r\n','\n'),'\n\n','\n');; +REPLACE(REPLACE(LOAD_FILE('DATADIR/test/t2.ini'),'\r\n','\n'),'\n\n','\n') +[2sec1] +val=2val1 +[2sec2] +val=2val2 + +DROP TABLE t1, t2; diff --git a/storage/connect/mysql-test/connect/r/mysql.result b/storage/connect/mysql-test/connect/r/mysql.result new file mode 100644 index 00000000000..f478e23881b --- /dev/null +++ b/storage/connect/mysql-test/connect/r/mysql.result @@ -0,0 +1,280 @@ +# +# Testing FILE privilege +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1remote (a INT NOT NULL); +INSERT INTO t1remote VALUES (10),(20),(30); +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'; +SELECT * FROM t1; +a +10 +20 +30 +SELECT user(); +user() +user@localhost +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO t1 VALUES ('xxx'); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1 WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a='yyy' WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1, t1remote; +DROP USER user@localhost; +# +# Testing FILE privileges done +# +CREATE TABLE t1 (a int, b char(10)); +INSERT INTO t1 VALUES (NULL,NULL),(0,'test00'),(1,'test01'),(2,'test02'),(3,'test03'); +SELECT * FROM t1; +a b +NULL NULL +0 test00 +1 test01 +2 test02 +3 test03 +# +# Testing errors +# +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root1,port=PORT'; +ERROR HY000: (1045) Access denied for user 'root1'@'localhost' (using password: NO) +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL DBNAME='unknown' TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +ERROR HY000: (1049) Unknown database 'unknown' +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL OPTION_LIST='host=localhost,user=root,port=PORT' DBNAME='unknown' TABNAME='t1'; +ERROR HY000: (1049) Unknown database 'unknown' +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='unknown' OPTION_LIST='host=localhost,user=root,port=PORT'; +ERROR HY000: (1146) Table 'test.unknown' doesn't exist [SHOW FULL COLUMNS FROM unknown FROM test] +SHOW CREATE TABLE t2; +ERROR 42S02: Table 'test.t2' doesn't exist +CREATE TABLE t2 (x int, y char(10)) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `x` int(11) DEFAULT NULL, + `y` char(10) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +ERROR HY000: Got error 174 '(1054) Unknown column 'x' in 'field list' [SELECT `x`, `y` FROM `t1`]' from CONNECT +DROP TABLE t2; +ALTER TABLE t1 RENAME t1backup; +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +ALTER TABLE t1backup RENAME t1; +# +# Testing SELECT, etc. +# +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `b` varchar(10) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a b +NULL NULL +0 test00 +1 test01 +2 test02 +3 test03 +DROP TABLE t2; +CREATE TABLE t2 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL, + `b` char(10) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a b +NULL NULL +0 test00 +1 test01 +2 test02 +3 test03 +DROP TABLE t2; +CREATE TABLE t2 (a INT NOT NULL, b CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) NOT NULL, + `b` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a b +0 +0 test00 +1 test01 +2 test02 +3 test03 +DROP TABLE t2; +CREATE TABLE t2 (a char(10), b int) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` char(10) DEFAULT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a b +NULL NULL +0 0 +1 0 +2 0 +3 0 +DROP TABLE t2; +DROP TABLE t1; +# +# Testing numeric data types +# +CREATE TABLE t1 (a smallint); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` smallint(6) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` smallint(6) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +CREATE TABLE t1 (a mediumint); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` mediumint(9) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(9) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +CREATE TABLE t1 (a int); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +CREATE TABLE t1 (a bigint); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` bigint(20) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +# +# Testing character data types +# +CREATE TABLE t1 (a char(10)); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(10) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +CREATE TABLE t1 (a varchar(10)); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(10) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` varchar(10) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; +# +# Testing binary data types +# +# +# Testing temporal data types +# +CREATE TABLE t1 (a date); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=PORT'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` date DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` date DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=MYSQL `TABNAME`='t1' `OPTION_LIST`='host=localhost,user=root,port=PORT' +SELECT * FROM t2; +a +DROP TABLE t2, t1; diff --git a/storage/connect/mysql-test/connect/r/null.result b/storage/connect/mysql-test/connect/r/null.result new file mode 100644 index 00000000000..0e4f1f864b9 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/null.result @@ -0,0 +1,134 @@ +# +# Testing FIX null columns +# +CREATE TABLE t1 +( +id INT NOT NULL, +nb INT, +msg VARCHAR(12) +) ENGINE=CONNECT TABLE_TYPE=FIX; +Warnings: +Warning 1105 No file name. Table will use t1.fix +INSERT INTO t1 values(NULL,1,'Hello'); +ERROR 23000: Column 'id' cannot be null +INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero'); +SELECT * FROM t1; +id nb msg +10 4 NULL +20 2 Hello +0 NULL Zero +SELECT* FROM t1 WHERE id IS NULL; +id nb msg +SELECT * FROM t1 WHERE nb IS NULL; +id nb msg +0 NULL Zero +SELECT * FROM t1 WHERE msg IS NOT NULL; +id nb msg +20 2 Hello +0 NULL Zero +DROP TABLE t1; +# +# Testing CSV null columns +# +CREATE TABLE t1 +( +id INT NOT NULL, +nb INT, +msg VARCHAR(12) +) ENGINE=CONNECT TABLE_TYPE=CSV HEADER=1; +Warnings: +Warning 1105 No file name. Table will use t1.csv +INSERT INTO t1 values(NULL,1,'Hello'); +ERROR 23000: Column 'id' cannot be null +INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero'); +SELECT * FROM t1; +id nb msg +10 4 NULL +20 2 Hello +0 NULL Zero +SELECT* FROM t1 WHERE id IS NULL; +id nb msg +SELECT * FROM t1 WHERE nb IS NULL; +id nb msg +0 NULL Zero +SELECT * FROM t1 WHERE msg IS NOT NULL; +id nb msg +20 2 Hello +0 NULL Zero +DROP TABLE t1; +# +# Testing BIN null columns +# +CREATE TABLE t1 +( +id INT NOT NULL, +nb INT, +msg VARCHAR(12) +) ENGINE=CONNECT TABLE_TYPE=BIN; +Warnings: +Warning 1105 No file name. Table will use t1.bin +INSERT INTO t1 values(NULL,1,'Hello'); +ERROR 23000: Column 'id' cannot be null +INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero'); +SELECT * FROM t1; +id nb msg +10 4 NULL +20 2 Hello +0 NULL Zero +SELECT* FROM t1 WHERE id IS NULL; +id nb msg +SELECT * FROM t1 WHERE nb IS NULL; +id nb msg +0 NULL Zero +SELECT * FROM t1 WHERE msg IS NOT NULL; +id nb msg +20 2 Hello +0 NULL Zero +DROP TABLE t1; +# +# Testing DBF null columns +# +CREATE TABLE t1 +( +id INT NOT NULL, +nb INT, +msg VARCHAR(12) +) ENGINE=CONNECT TABLE_TYPE=DBF; +Warnings: +Warning 1105 No file name. Table will use t1.dbf +INSERT INTO t1 values(NULL,1,'Hello'); +ERROR 23000: Column 'id' cannot be null +INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero'); +SELECT * FROM t1; +id nb msg +10 4 NULL +20 2 Hello +0 NULL Zero +SELECT* FROM t1 WHERE id IS NULL; +id nb msg +SELECT * FROM t1 WHERE nb IS NULL; +id nb msg +0 NULL Zero +SELECT * FROM t1 WHERE msg IS NOT NULL; +id nb msg +20 2 Hello +0 NULL Zero +DROP TABLE t1; +# +# Testing INI null columns +# +CREATE TABLE t1 +( +`sec` char(8) NOT NULL flag=1, +`key` char(12) +) ENGINE=CONNECT TABLE_TYPE=INI; +Warnings: +Warning 1105 No file name. Table will use t1.ini +INSERT INTO t1(sec) values('S1'); +SELECT * FROM t1; +sec key +INSERT INTO t1 values('S1','Newval'); +SELECT * FROM t1; +sec key +S1 Newval +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/odbc_sqlite3.result b/storage/connect/mysql-test/connect/r/odbc_sqlite3.result new file mode 100644 index 00000000000..093d64436c2 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/odbc_sqlite3.result @@ -0,0 +1,72 @@ +Table Create Table +t1 CREATE TABLE `t1` ( + `Description` varchar(128) NOT NULL, + `Attributes` varchar(256) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=ODBC `CATFUNC`=Drivers +SET NAMES utf8; +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Drivers; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Sources; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='Driver=SQLite3 ODBC Driver;Database=MYSQL_TEST_DIR/suite/connect/std_data/test.sqlite3;NoWCHAR=yes' CHARSET=utf8 DATA_CHARSET=utf8;; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(64) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=utf8 CONNECTION='Driver=SQLite3 ODBC Driver;Database=MYSQL_TEST_DIR/suite/connect/std_data/test.sqlite3;NoWCHAR=yes' `TABLE_TYPE`=ODBC `DATA_CHARSET`=utf8 +SELECT * FROM t1; +a +test1 +test2 +теÑÑ‚1 +теÑÑ‚2 +ÆÇÈÉË +SELECT user(); +user() +user@localhost +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO t1 VALUES ('xxx'); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1 WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a='yyy' WHERE a='xxx'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +DROP USER user@localhost; diff --git a/storage/connect/mysql-test/connect/r/odbc_xls.result b/storage/connect/mysql-test/connect/r/odbc_xls.result new file mode 100644 index 00000000000..f5c580186cf --- /dev/null +++ b/storage/connect/mysql-test/connect/r/odbc_xls.result @@ -0,0 +1,15 @@ +Table Create Table +t1 CREATE TABLE `t1` ( + `Name` varchar(256) NOT NULL, + `Description` varchar(256) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=ODBC `CATFUNC`=Sources +CREATE TABLE contact (Nom VARCHAR(128), Fonction VARCHAR(128), Company VARCHAR(128), Repertoire VARCHAR(30)) ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='DSN=ConnectEngineXLS;DBQ=DATADIR/test/contacts.xls';; +SELECT Nom, Fonction FROM contact WHERE Repertoire='ascii'; +Nom Fonction +Du Halgouet Tanguy NULL +Vandamme Anna NULL +Thomas Willy NULL +Thomas Dominique NULL +Lemonnier Nathalie Directeur Marketing Client +Menseau Eric NULL +DROP TABLE contact; diff --git a/storage/connect/mysql-test/connect/r/secure_file_priv.result b/storage/connect/mysql-test/connect/r/secure_file_priv.result new file mode 100644 index 00000000000..4acc0fd35ce --- /dev/null +++ b/storage/connect/mysql-test/connect/r/secure_file_priv.result @@ -0,0 +1,8 @@ +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='DATADIR/t1.dbf'; +ERROR HY000: The MariaDB server is running with the --secure-file-priv option so it cannot execute this statement +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='SECUREDATADIR/t1.dbf'; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/tbl.result b/storage/connect/mysql-test/connect/r/tbl.result new file mode 100644 index 00000000000..52b749acec4 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/tbl.result @@ -0,0 +1,147 @@ +# +# Checking TBL tables +# +CREATE TABLE t1 ( +a INT NOT NULL, +message CHAR(10)) ENGINE=connect; +Warnings: +Warning 1105 No table_type. Was set to DOS +Warning 1105 No file name. Table will use t1.dos +INSERT INTO t1 VALUES (1,'Testing'),(2,'dos table'),(3,'t1'); +SELECT * FROM t1; +a message +1 Testing +2 dos table +3 t1 +CREATE TABLE t2 ( +a INT NOT NULL, +message CHAR(10)) ENGINE=connect TABLE_TYPE=BIN; +Warnings: +Warning 1105 No file name. Table will use t2.bin +INSERT INTO t2 VALUES (1,'Testing'),(2,NULL),(3,'t2'); +SELECT * FROM t2; +a message +1 Testing +2 NULL +3 t2 +CREATE TABLE t3 ( +a INT NOT NULL, +message CHAR(10)) ENGINE=connect TABLE_TYPE=CSV; +Warnings: +Warning 1105 No file name. Table will use t3.csv +INSERT INTO t3 VALUES (1,'Testing'),(2,'csv table'),(3,'t3'); +SELECT * FROM t3; +a message +1 Testing +2 csv table +3 t3 +CREATE TABLE t4 ( +ta INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +message CHAR(20)) ENGINE=MyISAM; +INSERT INTO t4 (message) VALUES ('Testing'),('myisam table'),('t4'); +SELECT * FROM t4; +ta message +1 Testing +2 myisam table +3 t4 +CREATE TABLE total ( +tabname CHAR(8) NOT NULL SPECIAL='TABID', +ta TINYINT NOT NULL FLAG=1, +message CHAR(20)) +engine=CONNECT table_type=TBL table_list='t1,t2,t3,t4'; +select * from total; +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t2 1 Testing +t2 2 NULL +t2 3 t2 +t3 1 Testing +t3 2 csv table +t3 3 t3 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where tabname = 't2'; +tabname ta message +t2 1 Testing +t2 2 NULL +t2 3 t2 +select * from total where tabname = 't2' and ta = 3; +tabname ta message +t2 3 t2 +select * from total where tabname in ('t1','t4'); +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where ta = 3 and tabname in ('t1','t2'); +tabname ta message +t1 3 t1 +t2 3 t2 +select * from total where tabname <> 't2'; +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t3 1 Testing +t3 2 csv table +t3 3 t3 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where tabname != 't2' and ta = 3; +tabname ta message +t1 3 t1 +t3 3 t3 +t4 3 t4 +select * from total where tabname not in ('t2','t3'); +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where ta = 3 and tabname in ('t2','t3'); +tabname ta message +t2 3 t2 +t3 3 t3 +select * from total where ta = 3 or tabname in ('t2','t4'); +tabname ta message +t1 3 t1 +t2 1 Testing +t2 2 NULL +t2 3 t2 +t3 3 t3 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where not tabname = 't2'; +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t3 1 Testing +t3 2 csv table +t3 3 t3 +t4 1 Testing +t4 2 myisam table +t4 3 t4 +select * from total where tabname = 't2' or tabname = 't1'; +tabname ta message +t1 1 Testing +t1 2 dos table +t1 3 t1 +t2 1 Testing +t2 2 NULL +t2 3 t2 +DROP TABLE total; +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +DROP TABLE t4; diff --git a/storage/connect/mysql-test/connect/r/upd.result b/storage/connect/mysql-test/connect/r/upd.result new file mode 100644 index 00000000000..25ab886e551 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/upd.result @@ -0,0 +1,1627 @@ +CREATE TABLE employee +( +serialno CHAR(5) NOT NULL, +name VARCHAR(12) NOT NULL FLAG=6, +sex TINYINT(1) NOT NULL, +title VARCHAR(15) NOT NULL FLAG=20, +manager CHAR(5) NOT NULL, +department CHAR(4) NOT NULL FLAG=41, +secretary CHAR(5) NOT NULL FLAG=46, +salary DOUBLE(8,2) NOT NULL FLAG=52 +) ENGINE=connect TABLE_TYPE=fix FILE_NAME='employee.dat' ENDING=1; +SELECT * FROM employee; +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +CREATE PROCEDURE test.tst_up() DETERMINISTIC +BEGIN +SELECT * FROM t1; +UPDATE t1 SET salary = salary + 1, title = 'RESEARCH' WHERE title = 'SCIENTIST'; +UPDATE t1 SET salary = salary + 1, title = 'TECHNICIAN' WHERE title = 'ENGINEER'; +UPDATE t1 SET title = 'PUPPET' WHERE name = 'TONGHO'; +UPDATE t1 SET salary = 0. WHERE title = 'XXX'; +SELECT * FROM t1; +DELETE FROM t1 WHERE title = 'SECRETARY'; +DELETE FROM t1 WHERE title = 'DIRECTOR'; +DELETE FROM t1 WHERE title = 'TYPIST'; +SELECT * FROM t1; +DELETE FROM t1 LIMIT 3; +INSERT INTO t1(serialno, name, title, salary) VALUES('66666','NEWMAN','ENGINEER',10000.80); +SELECT * FROM t1; +DROP TABLE t1; +END// +# +# Testing DOS table changes +# +CREATE TABLE t1 ENGINE=connect AS SELECT * FROM employee; +Warnings: +Warning 1105 No table_type. Was set to DOS +Warning 1105 No file name. Table will use t1.dos +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing DOS table changes +# +CREATE TABLE t1 ENGINE=connect mapped=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No table_type. Was set to DOS +Warning 1105 No file name. Table will use t1.dos +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing FIX table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.fix +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing FIX table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix mapped=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.fix +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing FIX table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix huge=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.fix +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing CSV table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=csv AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.csv +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing CSV table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=csv mapped=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.csv +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing DBF table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=dbf AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.dbf +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing DBF table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=dbf mapped=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.dbf +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing BIN table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.bin +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing BIN table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin mapped=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.bin +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing BIN table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin huge=yes AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.bin +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing VEC table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec MAX_ROWS=30 AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.vec +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing VEC table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec mapped=yes MAX_ROWS=30 AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.vec +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing VEC table changes +# +CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec huge=yes MAX_ROWS=30 AS SELECT * FROM employee; +Warnings: +Warning 1105 No file name. Table will use t1.vec +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN 0 ENGINEER 10000.80 +# +# Testing INI table changes +# +CREATE TABLE t1 +( +serialno CHAR(5) NOT NULL FLAG=1, +name VARCHAR(12) NOT NULL, +sex TINYINT(1), +title VARCHAR(15) NOT NULL, +manager CHAR(5), +department CHAR(4), +secretary CHAR(5), +salary DOUBLE(8,2) NOT NULL +) ENGINE=connect TABLE_TYPE=ini; +Warnings: +Warning 1105 No file name. Table will use t1.ini +INSERT INTO t1 SELECT * FROM employee; +CALL test.tst_up(); +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 TECHNICIAN 31416 2452 11111 9001.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +serialno name sex title manager department secretary salary +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 RESEARCH 31416 2452 11111 8001.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 TECHNICIAN 70012 0318 24888 7401.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 PUPPET 70012 0318 24888 6801.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +31416 ORELLY 1 TECHNICIAN 87777 2452 11111 13401.00 +36666 BIGHORN 1 RESEARCH 31416 2452 11111 11001.00 +00137 BROWNY 1 TECHNICIAN 40567 0319 12345 10501.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 TECHNICIAN 40567 0319 12345 10001.00 +66666 NEWMAN NULL ENGINEER NULL NULL NULL 10000.80 +# +# Testing XML table changes (must be in a separate test) +# +DROP PROCEDURE test.tst_up; +DROP TABLE employee; diff --git a/storage/connect/mysql-test/connect/r/vec.result b/storage/connect/mysql-test/connect/r/vec.result new file mode 100644 index 00000000000..9461966aef7 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/vec.result @@ -0,0 +1,236 @@ +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=VEC MAX_ROWS=100; +Warnings: +Warning 1105 No file name. Table will use t1.vec +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=VEC MAX_ROWS=100 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=VEC MAX_ROWS=100 FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=VEC MAX_ROWS=100; +Warnings: +Warning 1105 No file name. Table will use t1.vec +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +CREATE TABLE dir1 ( +spath VARCHAR(256) NOT NULL flag=1, +fname VARCHAR(256) NOT NULL, +ftype CHAR(4) NOT NULL, +size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*vec*'; +CREATE TABLE t1 +( +a INT NOT NULL, +b CHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=VEC FILE_NAME='t1vec'; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=VEC `FILE_NAME`='t1vec' +SELECT * FROM t1; +a b +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/t1vec1: No such file or directory +Warning 1105 Open(rb) error 2 on DATADIR/test/t1vec2: No such file or directory +INSERT INTO t1 VALUES (0,'test01'), (1,'test01'), (2,'test02'), (3,'test03'); +SELECT * FROM t1; +a b +0 test01 +1 test01 +2 test02 +3 test03 +SELECT a FROM t1; +a +0 +1 +2 +3 +SELECT b FROM t1; +b +test01 +test01 +test02 +test03 +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +fname ftype size +t1vec1 16 +t1vec2 40 +DROP TABLE t1; +CREATE TABLE t1 +( +a INT NOT NULL, +b CHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=VEC FILE_NAME='t1vec' MAX_ROWS=10; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 MAX_ROWS=10 `TABLE_TYPE`=VEC `FILE_NAME`='t1vec' +SELECT * FROM t1; +a b +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/t1vec: No such file or directory +SELECT a FROM t1; +a +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/t1vec: No such file or directory +SELECT b FROM t1; +b +Warnings: +Warning 1105 Open(rb) error 2 on DATADIR/test/t1vec: No such file or directory +INSERT INTO t1 VALUES (0,'test01'), (1,'test01'), (2,'test02'), (3,'test03'); +SELECT * FROM t1; +a b +0 test01 +1 test01 +2 test02 +3 test03 +SELECT a FROM t1; +a +0 +1 +2 +3 +SELECT b FROM t1; +b +test01 +test01 +test02 +test03 +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +fname ftype size +t1vec 1400 +t1vec .blk 8 +# +# Testing READONLY +# +ALTER TABLE t1 READONLY=yes; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 MAX_ROWS=10 `TABLE_TYPE`=VEC `FILE_NAME`='t1vec' `READONLY`=yes +INSERT INTO t1 VALUES (4,'test04'); +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +UPDATE t1 SET b='test04' WHERE a=3; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +DELETE FROM t1 WHERE a=3; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +TRUNCATE TABLE t1; +ERROR HY000: Got error 174 'Cannot modify this read/only protected table' from CONNECT +ALTER TABLE t1 READONLY=no; +Warnings: +Warning 1105 The current version of CONNECT did not check what you changed in ALTER. Use at your own risk +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` char(10) NOT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 MAX_ROWS=10 `TABLE_TYPE`=VEC `FILE_NAME`='t1vec' `READONLY`=no +INSERT INTO t1 VALUES (4,'test04'); +UPDATE t1 SET b='test04a' WHERE a=4; +DELETE FROM t1 WHERE a=0; +SELECT * FROM t1; +a b +1 test01 +2 test02 +3 test03 +4 test04a +TRUNCATE TABLE t1; +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +fname ftype size +t1vec 0 +t1vec .blk 8 +SELECT * FROM t1; +a b +DROP TABLE t1; +# +# Clean up +# +DROP TABLE dir1; diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result new file mode 100644 index 00000000000..e7dcf8f4c73 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/xml.result @@ -0,0 +1,522 @@ +Warnings: +Warning 1105 No file name. Table will use t1.xml +# +# Beginning of grant.inc +# +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +SELECT user(); +user() +user@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2,rownode=row'; +Warnings: +Warning 1105 No file name. Table will use t1.xml +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +a +10 +UPDATE t1 SET a=20; +SELECT * FROM t1; +a +20 +DELETE FROM t1; +SELECT * FROM t1; +a +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +a +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +a +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2,rownode=row' FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2,rownode=row' FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +INSERT INTO t1 VALUES (10); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE t1 SET a=20; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +TRUNCATE TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 READONLY=1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +ALTER TABLE t1 FILE_NAME='t2.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +CREATE VIEW v1 AS SELECT * FROM t1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +SELECT user(); +user() +root@localhost +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +INSERT INTO v1 VALUES (2); +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +UPDATE v1 SET a=123; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DELETE FROM v1; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +SELECT user(); +user() +root@localhost +DROP VIEW v1; +DROP TABLE t1; +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2,rownode=row'; +Warnings: +Warning 1105 No file name. Table will use t1.xml +INSERT INTO t1 VALUES (10); +SELECT user(); +user() +user@localhost +ALTER TABLE t1 FILE_NAME='t1.EXT'; +ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +DROP TABLE t1; +DROP USER user@localhost; +# +# End of grant.inc +# +SET NAMES utf8; +# +# Testing tag values +# +CREATE TABLE t1 +( +AUTHOR CHAR(50), +TITLE CHAR(32), +TRANSLATOR CHAR(40), +PUBLISHER CHAR(40), +DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +AUTHOR Jean-Christophe Bernadac +TITLE Construire une application XML +TRANSLATOR NULL +PUBLISHER Eyrolles Paris +DATEPUB 1999 +AUTHOR William J. Pardi +TITLE XML en Action +TRANSLATOR James Guerin +PUBLISHER Microsoft Press Paris +DATEPUB 1999 +DROP TABLE t1; +# +# Testing that tag names are case sensitive +# +CREATE TABLE t1 +( +author CHAR(50), +TITLE CHAR(32), +TRANSLATOR CHAR(40), +PUBLISHER CHAR(40), +DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +author NULL +TITLE Construire une application XML +TRANSLATOR NULL +PUBLISHER Eyrolles Paris +DATEPUB 1999 +author NULL +TITLE XML en Action +TRANSLATOR James Guerin +PUBLISHER Microsoft Press Paris +DATEPUB 1999 +DROP TABLE t1; +# +# Testing attribute values +# +CREATE TABLE t1 ( +ISBN CHAR(15), +LANG CHAR(2), +SUBJECT CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='Coltype=@,xmlsup=libxml2'; +SELECT * FROM t1; +ISBN 9782212090819 +LANG fr +SUBJECT applications +ISBN 9782840825685 +LANG fr +SUBJECT applications +DROP TABLE t1; +# +# Testing that attribute names are case sensitive +# +CREATE TABLE t1 ( +isbn CHAR(15), +LANG CHAR(2), +SUBJECT CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='Coltype=@,xmlsup=libxml2'; +SELECT * FROM t1; +isbn NULL +LANG fr +SUBJECT applications +isbn NULL +LANG fr +SUBJECT applications +DROP TABLE t1; +# +# Testing mixed tag and attribute values +# +CREATE TABLE t1 ( +ISBN CHAR(15) FIELD_FORMAT='@', +LANG CHAR(2) FIELD_FORMAT='@', +SUBJECT CHAR(32) FIELD_FORMAT='@', +AUTHOR CHAR(50), +TITLE CHAR(32), +TRANSLATOR CHAR(40), +PUBLISHER CHAR(40), +DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +ISBN 9782212090819 +LANG fr +SUBJECT applications +AUTHOR Jean-Christophe Bernadac +TITLE Construire une application XML +TRANSLATOR NULL +PUBLISHER Eyrolles Paris +DATEPUB 1999 +ISBN 9782840825685 +LANG fr +SUBJECT applications +AUTHOR William J. Pardi +TITLE XML en Action +TRANSLATOR James Guerin +PUBLISHER Microsoft Press Paris +DATEPUB 1999 +DROP TABLE t1; +# +# Testing INSERT on mixed tag and attribute values +# +CREATE TABLE t1 ( +ISBN CHAR(15) FIELD_FORMAT='@', +LANG CHAR(2) FIELD_FORMAT='@', +SUBJECT CHAR(32) FIELD_FORMAT='@', +AUTHOR CHAR(50), +TITLE CHAR(32), +TRANSLATOR CHAR(40), +PUBLISHER CHAR(40), +DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.xml' + TABNAME='BIBLIO' + OPTION_LIST='rownode=BOOK,xmlsup=libxml2'; +INSERT INTO t1 (ISBN, LANG, SUBJECT, AUTHOR, TITLE, PUBLISHEr, DATEPUB) +VALUES('9782212090529','fr','général','Alain Michard', +'XML, Langage et Applications','Eyrolles Paris',1998); +SELECT * FROM t1; +ISBN 9782212090819 +LANG fr +SUBJECT applications +AUTHOR Jean-Christophe Bernadac +TITLE Construire une application XML +TRANSLATOR NULL +PUBLISHER Eyrolles Paris +DATEPUB 1999 +ISBN 9782840825685 +LANG fr +SUBJECT applications +AUTHOR William J. Pardi +TITLE XML en Action +TRANSLATOR James Guerin +PUBLISHER Microsoft Press Paris +DATEPUB 1999 +ISBN 9782212090529 +LANG fr +SUBJECT général +AUTHOR Alain Michard +TITLE XML, Langage et Applications +TRANSLATOR NULL +PUBLISHER Eyrolles Paris +DATEPUB 1998 +SELECT LOAD_FILE('test/xsample2.xml'); +LOAD_FILE('test/xsample2.xml') <?xml version="1.0" encoding="UTF-8"?> +<BIBLIO SUBJECT="XML"> + <BOOK ISBN="9782212090819" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>Jean-Christophe</FIRSTNAME> + <LASTNAME>Bernadac</LASTNAME> + </AUTHOR> + <AUTHOR> + <FIRSTNAME>François</FIRSTNAME> + <LASTNAME>Knab</LASTNAME> + </AUTHOR> + <TITLE>Construire une application XML</TITLE> + <PUBLISHER> + <NAME>Eyrolles</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> + <BOOK ISBN="9782840825685" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>William J.</FIRSTNAME> + <LASTNAME>Pardi</LASTNAME> + </AUTHOR> + <TRANSLATOR PREFIX="adapté de l'anglais par"> + <FIRSTNAME>James</FIRSTNAME> + <LASTNAME>Guerin</LASTNAME> + </TRANSLATOR> + <TITLE>XML en Action</TITLE> + <PUBLISHER> + <NAME>Microsoft Press</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> + <BOOK ISBN="9782212090529" LANG="fr" SUBJECT="général"> + <AUTHOR>Alain Michard</AUTHOR> + <TITLE>XML, Langage et Applications</TITLE> + <PUBLISHER>Eyrolles Paris</PUBLISHER> + <DATEPUB>1998</DATEPUB> + </BOOK> +</BIBLIO> + +DROP TABLE t1; +# +# Testing XPath +# +CREATE TABLE t1 ( +isbn CHAR(15) FIELD_FORMAT='@ISBN', +language CHAR(2) FIELD_FORMAT='@LANG', +subject CHAR(32) FIELD_FORMAT='@SUBJECT', +authorfn CHAR(20) FIELD_FORMAT='AUTHOR/FIRSTNAME', +authorln CHAR(20) FIELD_FORMAT='AUTHOR/LASTNAME', +title CHAR(32) FIELD_FORMAT='TITLE', +translated CHAR(32) FIELD_FORMAT='TRANSLATOR/@PREFIX', +tranfn CHAR(20) FIELD_FORMAT='TRANSLATOR/FIRSTNAME', +tranln CHAR(20) FIELD_FORMAT='TRANSLATOR/LASTNAME', +publisher CHAR(20) FIELD_FORMAT='PUBLISHER/NAME', +location CHAR(20) FIELD_FORMAT='PUBLISHER/PLACE', +year INT(4) FIELD_FORMAT='DATEPUB' +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1,xmlsup=libxml2'; +SELECT * FROM t1; +isbn 9782212090819 +language fr +subject applications +authorfn Jean-Christophe +authorln Bernadac +title Construire une application XML +translated NULL +tranfn NULL +tranln NULL +publisher Eyrolles +location Paris +year 1999 +isbn 9782840825685 +language fr +subject applications +authorfn William J. +authorln Pardi +title XML en Action +translated adapté de l'anglais par +tranfn James +tranln Guerin +publisher Microsoft Press +location Paris +year 1999 +SELECT isbn, title, translated, tranfn, tranln, location FROM t1 +WHERE translated <> ''; +isbn 9782840825685 +title XML en Action +translated adapté de l'anglais par +tranfn James +tranln Guerin +location Paris +DROP TABLE t1; +# +# Testing that XPath is case sensitive +# +CREATE TABLE t1 +( +isbn CHAR(15) FIELD_FORMAT='@isbn' +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1,xmlsup=libxml2'; +SELECT * FROM t1; +isbn NULL +isbn NULL +DROP TABLE t1; +# +# Testing character sets +# +CREATE TABLE t1 +( +c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2' + DATA_CHARSET=latin1; +ERROR HY000: DATA_CHARSET='latin1' is not supported for TABLE_TYPE=XML +CREATE TABLE t1 +( +c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2' + DATA_CHARSET=utf8; +SHOW CREATE TABLE t1; +Table t1 +Create Table CREATE TABLE `t1` ( + `c` char(16) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`=XML `FILE_NAME`='latin1.xml' `OPTION_LIST`='xmlsup=libxml2' `DATA_CHARSET`=utf8 +SELECT c, HEX(c) FROM t1; +c ÃÂÃÄÅÆÇ +HEX(c) C1C2C3C4C5C6C7 +DROP TABLE t1; +CREATE TABLE t1 +( +c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +c ÃÂÃÄÅÆÇ +HEX(c) C1C2C3C4C5C6C7 +DROP TABLE t1; +CREATE TABLE t1 +( +c CHAR(16) CHARACTER SET utf8 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +c ÃÂÃÄÅÆÇ +HEX(c) C381C382C383C384C385C386C387 +DROP TABLE t1; +# +# Conversion from latin1 to cp1251 produces a warning. +# Question marks are returned. +# +CREATE TABLE t1 +( +c CHAR(16) CHARACTER SET cp1251 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +c ??????? +HEX(c) 3F3F3F3F3F3F3F +Warnings: +Level Warning +Code 1366 +Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column 'c' at row 1 +DROP TABLE t1; +# +# Testing Cyrillic +# +CREATE TABLE t1 +( +c CHAR(16) CHARACTER SET utf8 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='cp1251.xml' + OPTION_LIST='xmlsup=libxml2,rownode=b'; +SELECT * FROM t1; +c БВГДЕЖЗ +INSERT INTO t1 VALUES ('ИКЛМÐ'); +SELECT c, HEX(c) FROM t1; +c БВГДЕЖЗ +HEX(c) D091D092D093D094D095D096D097 +c ИКЛМР+HEX(c) D098D09AD09BD09CD09D +DROP TABLE t1; +CREATE TABLE t1 +( +c CHAR(16) CHARACTER SET cp1251 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='cp1251.xml' + OPTION_LIST='xmlsup=libxml2,rownode=b'; +SELECT * FROM t1; +c БВГДЕЖЗ +c ИКЛМР+INSERT INTO t1 VALUES ('ОПРСТ'); +SELECT c, HEX(c) FROM t1; +c БВГДЕЖЗ +HEX(c) C1C2C3C4C5C6C7 +c ИКЛМР+HEX(c) C8CACBCCCD +c ОПРСТ +HEX(c) CECFD0D1D2 +DROP TABLE t1; +# +# Testing that the underlying file is created with a proper Encoding +# +CREATE TABLE t1 (node VARCHAR(50)) +CHARACTER SET latin1 +ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=utf-8'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +SELECT node, hex(node) FROM t1; +node ÀÃÂà +hex(node) C0C1C2C3 +DROP TABLE t1; +SET @a=LOAD_FILE('test/t1.xml'); +SELECT LEFT(@a,38); +LEFT(@a,38) <?xml version="1.0" encoding="utf-8"?> +SELECT HEX(EXTRACTVALUE(@a,'/t1/line/node')); +HEX(EXTRACTVALUE(@a,'/t1/line/node')) C380C381C382C383 +CREATE TABLE t1 (node VARCHAR(50)) +CHARACTER SET latin1 +ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=iso-8859-1'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +SELECT node, hex(node) FROM t1; +node ÀÃÂà +hex(node) C0C1C2C3 +DROP TABLE t1; +SET @a=LOAD_FILE('test/t1.xml'); +SELECT LEFT(@a,43); +LEFT(@a,43) <?xml version="1.0" encoding="iso-8859-1"?> +SELECT HEX(EXTRACTVALUE(@a,'/t1/line/node')); +HEX(EXTRACTVALUE(@a,'/t1/line/node')) C0C1C2C3 +# +# Testing XML entities +# +CREATE TABLE t1 (node VARCHAR(50)) +CHARACTER SET utf8 +ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=iso-8859-1'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +INSERT INTO t1 VALUES (_cp1251 0xC0C1C2C3); +INSERT INTO t1 VALUES ('&<>"\''); +SELECT node, hex(node) FROM t1; +node ÀÃÂà +hex(node) C380C381C382C383 +node ÐБВГ +hex(node) D090D091D092D093 +node &<>"' +hex(node) 263C3E2227 +DROP TABLE t1; +SET @a=LOAD_FILE('test/t1.xml'); +SELECT CAST(@a AS CHAR CHARACTER SET latin1); +CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?> +<!-- Created by CONNECT Version 1.01.0004 April 10, 2013 --> +<t1> + <line> + <node>ÀÃÂÃ</node> + </line> + <line> + <node>АБВГ</node> + </line> + <line> + <node>&<>"'</node> + </line> +</t1> + diff --git a/storage/connect/mysql-test/connect/std_data/Testbal.dat b/storage/connect/mysql-test/connect/std_data/Testbal.dat Binary files differnew file mode 100644 index 00000000000..b3f0e26e5a5 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/Testbal.dat diff --git a/storage/connect/mysql-test/connect/std_data/boys.txt b/storage/connect/mysql-test/connect/std_data/boys.txt new file mode 100644 index 00000000000..ada3b2dc45c --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/boys.txt @@ -0,0 +1,6 @@ +John Boston 25/01/1986 02/06/2010 +Henry Boston 07/06/1987 01/04/2008 +George San Jose 10/08/1981 02/06/2010 +Sam Chicago 22/11/1979 10/10/2007 +James Dallas 13/05/1992 14/12/2009 +Bill Boston 11/09/1986 10/02/2008 diff --git a/storage/connect/mysql-test/connect/std_data/boyswin.txt b/storage/connect/mysql-test/connect/std_data/boyswin.txt new file mode 100644 index 00000000000..bf6eee6238c --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/boyswin.txt @@ -0,0 +1,6 @@ +John Boston 25/01/1986 02/06/2010
+Henry Boston 07/06/1987 01/04/2008
+George San Jose 10/08/1981 02/06/2010
+Sam Chicago 22/11/1979 10/10/2007
+James Dallas 13/05/1992 14/12/2009
+Bill Boston 11/09/1986 10/02/2008
diff --git a/storage/connect/mysql-test/connect/std_data/contact.ini b/storage/connect/mysql-test/connect/std_data/contact.ini new file mode 100644 index 00000000000..6d11e738ec2 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/contact.ini @@ -0,0 +1,23 @@ +[BER] +name=Bertrand +forename=Olivier +address=21 rue Ferdinand Buisson +city=Issy-les-Mlx +zipcode=92130 +tel=09.54.36.29.60 +cell=06.70.06.04.16 +[WEL] +name=Schmitt +forename=Bernard +hired=19/02/1985 +address=64 tiergarten strasse +city=Berlin +zipcode=95013 +tel=03.43.377.360 +[UK1] +name=Smith +forename=Henry +hired=08/11/2003 +address=143 Blum Rd. +city=London +zipcode=NW1 2BP diff --git a/storage/connect/mysql-test/connect/std_data/contacts.xls b/storage/connect/mysql-test/connect/std_data/contacts.xls Binary files differnew file mode 100644 index 00000000000..77a97bef0f5 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/contacts.xls diff --git a/storage/connect/mysql-test/connect/std_data/cp1251.xml b/storage/connect/mysql-test/connect/std_data/cp1251.xml new file mode 100644 index 00000000000..af15d5f8fe8 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/cp1251.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="cp1251"?> +<a><b><c>ÁÂÃÄÅÆÇ</c></b></a> diff --git a/storage/connect/mysql-test/connect/std_data/dept.dat b/storage/connect/mysql-test/connect/std_data/dept.dat new file mode 100644 index 00000000000..2cf0ba8c428 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/dept.dat @@ -0,0 +1,4 @@ +0318 KINGSTON 70012 SALES Bank/Insurance +0021 ARMONK 87777 CHQ Corporate headquarter +0319 HARRISON 40567 SALES Federal Administration +2452 POUGHKEEPSIE 31416 DEVELOPMENT Research & development diff --git a/storage/connect/mysql-test/connect/std_data/emp.txt b/storage/connect/mysql-test/connect/std_data/emp.txt new file mode 100644 index 00000000000..08cafdb8cab --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/emp.txt @@ -0,0 +1,4545 @@ +5745ESCOURCHE BENEDICTE 21935 71962121994051834514275.50 0MTECHN
+9692VICENTE LAURENCE 21941 81967101989011621213032.80 0MANGL
+9146NICOLAS ROGER 11941 61964071995023417325098.65 0MSANS
+2985TESSEREAU MARIE HELENE 21941 91967011990011932314933.78 0VSANS
+3368MOGADOR ALAIN 11941 11961091993114330331420.55 0CSANS
+7394CHAUSSEE ERIC DENIS 11944 91965111983123200223583.86 0MANGL
+4655MAILLOT GEORGES 11945 51970091986122470018541.64 0CANGL
+2825CAMILLE NADINE 21956 91994011993011949415050.45 0MSANS
+1460BRUYERES JEAN MARC 11958 81984081988052090215980.07 0MSANS
+4974LONES GERARD 11959101979011994121608112916.70 0MSANS
+8811CROISILLES DOMINIQUE 11961101983051991011868714507.13 0MSANS
+4682MUY CHRISTINE 21962101986051985051288210745.30 0MANGL
+2974SEYSSAUD GERARD 11963 91984101989051949415050.55 0MALLEM
+3185QUINET OLIVIER 11966 71985061993111868714507.94 0MSANS
+9543KOMITAS YVES 11967 21993011992011330611017.70 0MTECHN
+3493COUDET ERIC CHRISTIAN 11935101955041992063417325097.97 1MTECHN
+9039CHAMBOURCY JEAN PAUL 11936 21965031995033417325098.51 1MALLEM
+3731CLERE BRIGITTE 21936111963091995082666519900.77 1CSANS
+2935BONVIN ROBERT 11937 71960121985042273717261.66 1MANGL
+4278VALBOSQUET VERONIQUE SIMONE 21939 21972071992021932314933.54 1MSANS
+1653LIEGE LAURENCE 2193912198901198605 8656 8145.57 1.SANS
+4477LECOURBE DIDIER 11939121965051993011663613342.67 1MSANS
+1400AVENE ANDRE 11940111969121993041557012568.44 1MALLEM
+9742YZENGREMER MICHEL 11941121968011995042201216756.19 1MVENTE
+7255PEYRESTORTES MARIE CHRISTINE 21941 11962041988072470018541.83 1MANGL
+1725EPEND MARIE AGNES 21941 41973071989011621213033.65 1VSANS
+4647PERADONNERIE JACQUES 11941121974071992071369311287.41 1MSANS
+7633SCHEFFER CLAUDE 11944 11969031991011433211714.46 1MSANS
+9793SABLONS MARIE FRANCE 21945 5199408199308 9126 8456.87 1VSANS
+6057EAU BRUNO 11947 91972041991071433211715.33 1MSANS
+6465JADES CATHERINE 21947121970011995042137316331.60 1MINFOR
+8298BERGERS BERNARD 11947101975091993011369311287.29 1MSANS
+9254LEGROS SUZANNE 21947 41971111995111684813422.57 1MESP
+7761KAMAL ALAIN 11948 91977041993111471512024.08 1MCOMPT
+5772GAUTHIER SABINE 21948 61977071994041471512025.34 1MTECHN
+9643BARTHELEMY DOMINIQUE 11949 519871019901111131 9657.16 1LSANS
+3906ROUYRE LAURE 21949 31973071972071326611016.21 1MSANS
+8707SICILE JEAN PIERRE 11950 11976071993011369311286.56 1MANGL
+0091THIVERNAL DIDIER JEAN 11951101980051991101471512024.71 1MSANS
+5757CLOPERIE FABIENNE 21951 71972051995071684813423.26 1MSANS
+7795LATOUR CATHERINE 21952 4199406199306 8359 7953.72 1MSANS
+4879EGUILLES GILLES 11952 61972121990071621213032.51 1MANGL
+8311VINS JEAN PIERRE 11952 119930919920910407 9272.06 1MTECHN
+5964JUST STEPHANE BERNARD 11952 51974061993011369311286.48 1MSANS
+2822NEMAN ELISABETH 21952 7199304199204 9597 8728.14 1MSANS
+1340ABBE MICHELE 21953101973071991041578112722.60 1MSANS
+5357ZOLA BERNARD 11954 41980061992011471512024.35 1MSANS
+8456DHUYS CORINNE ANNE 21954 71979031993041471512025.50 1MINFOR
+4618FERRY BRUNO 11954 41977041995011578112722.24 1MSANS
+8212CIRCLE BERNARD 11955111975041991071578112722.42 1MSANS
+8015BACHELET DOMINIQUE 11956121977031994072644919744.10 1MTECHN
+4320SUPERI MONIQUE 21956 319940519920111389 9852.00 1MSANS
+0442CONTOUR ALAIN 11957 91977031995062201216757.70 1MSANS
+1034PROLONGEE MICHELINE 21957 61980051994071471512024.15 1MSANS
+2106BARDE JEAN CLAUDE 11958 219840719830711131 9659.17 1MANGL
+1977FOCH BERNADETTE 21958 3199202199102 8656 8145.03 1.SANS
+3620BAGNOLET ANDRE 11958 11978061992051471512024.33 1MSANS
+3344PEUPLIERS JOSIANE 21959 8199207199201 8656 8146.16 1DSANS
+9038MALVILLE ETHEL LINKA 21959 31993091992011202810240.59 1MTECHN
+0475PIERREFONTAINE BERNARD 11959 61984081987112090215979.62 1MSANS
+4077MONTMELIAN NOELLE 21960 21984031983031288210743.92 1MSANS
+1421FONTCHANDON PATRICE 11961 71984081989052090215980.97 1MALLEM
+7043MOULERON MARTINE 21962 5199303199203 8656 8145.83 1.SANS
+7239EPARGNE PASCALE 21962 91983011982011608112915.57 1MSANS
+0361BRUANT LAURENCE 21962 31983041994091894014661.48 1CSANS
+1988MIGNARD ANNE 21962 51993081992011249910473.36 1MSANS
+8159GOLFE JEAN CLAUDE 11963 81985101992071288210745.49 1MANGL
+7227BLEU MICHELE 21963 21994071992011249910472.61 1MTECHN
+2721SATGE SOPHIE JULIE 21963 6199503199403 8656 8146.07 1MESP
+1514PANNES JOEL ETIENNE 11964 11984101983101288210743.81 1MSANS
+1595SEPTEMBRE PATRICE 11964 11988031987031527112373.40 1MSANS
+5702CONCORDE PAUL 11965 71994051994011288210744.94 1MMARKT
+3500DRIONNE JEAN XAVIER 119651119860119850110791 9501.81 1MSANS
+3597PIERAM DOMINIQUE 11966 31991101990101381911366.72 1MINFOR
+3537JEAN ANNE MARIE 2196610199610199510 9894 8921.90 1MSANS
+0036CHATEAUBRIAND FRANCOISE 21967 8199307199207 7803 7681.42 1.SANS
+2501GASTA PIERRE 11967101987091988071949415051.95 1CESP
+5621KERGUILLE PASCAL 11967 71988071994061381911364.83 1MALLEM
+5823BRIAC MONIQUE 21968 51994011993011360611210.93 1MSANS
+3717FRADINIERE DANIEL 11968 51988041991041249910472.01 1MTECHN
+2374PETITS SYLVIE FRANCOISE 21969 91992091991091330611018.24 1MSANS
+8139POMPE ERIC 11969 21989041995031249910472.21 1MESP
+3293GUSTAVIA MICHEL 11969 11993021992021420611637.74 1CINFOR
+6848IMPASSE CLIVE 11969 71988071994011249910471.88 1MSANS
+2857ERMITE DOMINIQUE 11969 319910719900710407 9271.86 1MTECHN
+9167PARENT JEAN CLAUDE 11969 919930419920411389 9850.61 1CTECHN
+6582OR MARYSE 21970 419920719910710024 8998.62 1MSANS
+3685ANNEAU CORINNE JEANNE 21970 919920819910811389 9852.27 1MSANS
+4890CIMIEZ PHILIPPE 11971121994101993101360611209.89 1MSANS
+7740MARCEAU AGNES 21971 119920919910911389 9852.45 1MANGL
+5934RANCHO DOMINIQUE 11971 91992071991071420611637.60 1CANGL
+6784WEST JEAN LOUIS 11971 71995011994031202810241.57 1CSANS
+0502ARRALLES GILLES 11971121993121994011249910473.27 1CSANS
+5900LESIGNY OLIVIER 11972 6199606199506 8656 8145.74 1CVENTE
+9357EGLISE ROGER 11972 5199207199312 9597 8728.89 1CINFOR
+2647MONASTERE PHILIPPE JEAN 11972 919940619930610363 9233.31 1CSANS
+7752RANCE GERARD 11973 2199607199507 7803 7680.14 1CINFOR
+2565AQUITAINE GENEVIEVE 21973 119940619920111389 9852.22 1CALLEM
+6946ORBAY CHRISTIANE 2197311199207199101 8656 8146.88 1MSANS
+3358SALONINA ETIENNE 11973 419930919920911389 9852.36 1CANGL
+7527BOULEYGUE PATRICK PIERRE 119741219940419930410668 9426.72 1CSANS
+4780VALMORE ALBERT 11974 81995011994011206910241.16 1CSANS
+6139PALMAROLE BERNADETTE 21975 1199504199404 8359 7952.78 1CSANS
+2461CHAFFARD FREDERIC 11975 1199408199308 7803 7680.62 1CANGL
+4422KAZ ELISABETH 2197612199607199507 7803 7681.11 1CSANS
+3477CABRINE SYLVIE 21978 7199608199508 7803 7679.51 1CTECHN
+6323FABIA RENE 11935 31960121990082273717260.44 2MSANS
+5986ANNA DANIEL 11935 41970041991011433211714.88 2DSANS
+1512FONTAIN HENRY 1193511198901198801 9894 8922.57 2VESP
+6902BLANCS BRUNO 11935 61958071993052470018541.64 2MSANS
+1158CORNEBARRIEU JEAN FRANCOIS MARCEL11936121964011994011557012566.45 2CTECHN
+8277PRIEURE CORINNE 21936 51963011991072273717260.26 2CANGL
+3981TERME FREDERIC 11937 71959021989013844228044.89 2MSANS
+1713DANVILLE DANIEL 11937 51961101985022470018540.87 2MANGL
+9871CAZALIS THIERRY 11938 61966121989062947921840.15 2MANGL
+0383CRESSELY JOELLE 21938111962091990012273717260.55 2VANGL
+3742PYRENEES CHRISTIAN 11938121971101995011663613342.85 2MESP
+6595BAGUIERE FRANCOIS 11938121964081983102022115556.35 2MALLEM
+6543PIERRON PATRICK 11939 7198901198801 9894 8921.33 2CSANS
+8167CLAUX MARTINE 21939 21967011992021932314934.48 2CSANS
+4406LESBACHES COLETTE 21940111973071994041894014662.43 2MSANS
+8258HARTMANN MARC 11940 31968061995011663613343.06 2MVENTE
+3848BARAUDIERE JEAN CLAUDE 11940 11964101990042739020404.83 2MANGL
+1529PICOURENC ANNY 21940 71967011989062470018541.86 2DVENTE
+3069PERIER MICHELE 2194010199601199501 7803 7681.25 2MSANS
+8473COLONIE PIERRETTE 21941 31968041991041932314933.58 2MINFOR
+0084NIEMEN ODILE 21941 51970071991101932314933.73 2CSANS
+6401ALLERAY ARLETTE 21941 21972121993071894014662.64 2CSANS
+8343LEDION FRANCOIS RENE 11941 71972031991071663613344.41 2MALLEM
+9940LAUNES LILIANE 2194210199005198606 8656 8145.83 2.SANS
+7584MARSAT SERGE 11942101964081994011557012567.26 2MSANS
+1051ROSAN STEPHANE 11942 51962041991072273717261.13 2MANGL
+8928POINCARE ANNE MARIE 21942 41965021994051834514275.50 2MANGL
+0874FRANCS KATHERIN 21942 61968031992021932314934.00 2MSANS
+3411CORDELIERS ELISABETH 219421119931119921110152 9116.90 2MSANS
+8736PERTUADE DIDIER 11943 31973071992041403411520.93 2MANGL
+5553OLIVIER CHANTAL 21943 71964071992102137316331.97 2MTECHN
+8502AUGUSTE JOCELYNE 21943101962121991072273717261.16 2CTECHN
+7314DRAIO TOMOKO 21943111969111991072273717260.71 2MSANS
+7892HAUTIL MARIE AGNES NICOLE 2194312199201199101 7803 7681.35 2.SANS
+1146RUES LAURENT 11943 51970041990032022115556.13 2MSANS
+5013POURNAY STEPHANE 11943 71969101991011433211714.01 2MMARKT
+1970SARREBOURG CHRISTOPHE 11943 91962121988122470018540.20 2MTECHN
+2665SAVOIE HELENE 21944 91968091986062470018540.53 2MSANS
+9470KALISTE SUZANNE 21944 31970071991041578112722.12 2DSANS
+7109PEREIRA JOSIANE 21944 71965011982102470018541.72 2CSANS
+0293COURDIMANCHE JEAN CLAUDE 11944 41967011991012273717260.50 2MSANS
+6308BASQUES ERIC WILLEM 11944 71970051988042273717261.30 2MSANS
+9758ROSSA ROBERT 11944 21976051991101369311286.71 2MSANS
+0368GEROFOSSE JOELLE 21944 5198810198306 8656 8146.34 2.ANGL
+3899SALIVE JACQUES 11945 81971051991071433211713.67 2MTECHN
+4807RONDINELLE NOELLE 21945 81968091993062470018540.42 2MSANS
+1251CAP CLAUDE 1194510198907198903 8656 8146.20 2.ANGL
+8624FREVILLE ALAIN 119451119820119810111389 9852.74 2MSANS
+2995ROI JEAN 11946 91972011990101433211715.50 2MANGL
+2130POMPE JEAN MARIE 11946 11972081991071578112722.03 2MSANS
+1524ANTARES CHRISTIAN 11946 41972011990011433211714.43 2MTECHN
+2651AUBE YVES 11946 71971071987041932314934.48 2MALLEM
+5389YVON CAROLE 21946101969031993031834514275.34 2CSANS
+5055BERTHIER CAROLE SIMONE 21947 319931019921010024 8999.31 2MINFOR
+9623LAGER NORMA 21947 61982071992121471512024.51 2MSANS
+9198CAGEAC MICHEL 11947101971031991011433211715.45 2MTECHN
+1117MONS JOELLE 21948 41970081986052334017609.84 2MSANS
+3883BELFORT FLORENCE 21948 2199503199403 8359 7952.96 2MINFOR
+1886CHERCHE CHRISTIANE 21948 3199412199312 9597 8729.72 2MSANS
+9452MORGANE ISABELLE HELENE 21948 61971061989011621213032.70 2MVENTE
+1240POSTEL JOHN 11948111974071992011403411520.33 2MTECHN
+2793DALHIAS EUGENE 11948 71974041992031518612336.02 2MSANS
+1264TRESSERVE EVELYNE 21948 71969081991012990722149.03 2MTECHN
+2964PUJADE ROBERT 11949 81974071992041403411521.74 2MANGL
+9832COUTURIER PATRICK 11949 11972031988102273717260.76 2MSANS
+7619ETIGNY FLORENCE 21949 21970111995042137316332.59 2DVENTE
+3262FABRONITA ANNE 21949 11969091993061834514274.66 2MSANS
+2312SENONCHES ERIK 11949111977041993041369311287.23 2MALLEM
+8743GALLA DANIEL 11949 31970081991012137316331.79 2MALLEM
+0857RAYNOUARD JEAN PIERRE 11950 91982061993111369311287.65 2MSANS
+2136DEROISIN JEAN MARIE 11950 11979061992011608112916.74 2CSANS
+4603LEPURDIE PHILIPPE 11950 91977101990101684813422.12 2MSANS
+2694JOSEPH ERIC 11950 31979021992011249910471.62 2SANGL
+0601SAOUVES PATRICIA 21950 51971021990101834514275.83 2MTECHN
+7409RENE DANIEL 11950 11973051990122470018541.83 2MANGL
+0950SENNELY JEAN PIERRE 11950 51977081986091621213033.28 2MANGL
+1214LOTHIERS ALAIN 11951 51976101993011608112915.65 2MSANS
+6388LAGRANGE ANDRE 11951 81976031984112470018541.20 2MSANS
+2773MORGON ALBAN 11951 91974051988122334017610.33 2MANGL
+7718PIEDS PASCAL BRUNO 11951 61981061993011471512025.91 2CSANS
+8483MEDITERRANEE FREDERIC 11951101970101991112334017610.56 2MSANS
+6772TRARIEUX GUY 11952 91976041992121932314933.54 2MTECHN
+3860MONTABO GHYSLAIN 21952121972041979101684813421.64 2MMICRO
+4868SEVRIER BERNARD 11952 31981041992121369311287.50 2MANGL
+5715CHEVREUSE CATHERINE 21953 11974081991092175716600.56 2MSANS
+3702CALIFO BEATRICE 21953 21973021993012017815517.91 2MESP
+4778LABORDE SHOKO 21953 61973041990101578112723.50 2MSANS
+4134GABONNAISE GHISLAINE 21953 61980071979071202810241.30 2DESP
+4174GAGNEUR JEAN FRANCIS 11953 81979011991101369311288.22 2CSANS
+6892NOLET JEAN LOUIS 11953 81982051992041369311287.41 2MSANS
+5817SERPENTINE CLAUDE 11954 11973111989122175716601.28 2MSANS
+9960GASSIN PASCAL 11954 81974121991012334017610.60 2MSANS
+4935ANNE EVELYNE 21954101975021986101684813423.38 2CANGL
+7050LUBECK MARIE CHRISTINE 21954 81975101993091608112915.33 2DESP
+4537ANNECY ROBERT 11954 91974061992011578112721.89 2MANGL
+1403CHAUVETS CHRISTINE 21954 31977031992011471512025.55 2MINFOR
+3733JAUSSERANNE MASAYO 21955 41978011981091834514274.29 2MSANS
+3799VAILLANT ANNE MARIE 2195558199606199506 7803 7679.58 2MSANS
+1112CHAUMINE RUTH 21956 71985091984091288210745.99 2MSANS
+8102AUSSOU MARCELINE 21956 91985061984061288210744.53 2MMICRO
+3321GABRIE JEAN LOUIS 11956101976031993041471512024.75 2MSANS
+6037SENART JEAN PIERRE 11956 419810319921011389 9852.47 2MSANS
+2073CHAUFOURNIERS FRANCOIS 11956 61976111994041471512026.24 2MSANS
+3246LAMANON VERONIQUE 21956111980041995101527112374.97 2MTECHN
+5182MA PIERRE 11957 41978121994071911114779.14 2MALLEM
+1023GIONO PATRICK 11957 91989011995011249910472.21 2MSANS
+6811GENERAL ALAIN 11957 219930719940111302 9775.56 2MSANS
+9879FOUJU PATRICK 11958 91983051987052090215979.70 2MSANS
+9605ATLANTIQUE CATHERINE 21958 4199510199410 7803 7681.20 2MSANS
+4610COLMAR YVES 11958111982021981021608112916.50 2MSANS
+1905INGRANDES GILLES 11958 11978061993041471512024.03 2MSANS
+6585VERON PHILIPPE 11958 41990091989111450311831.69 2MINFOR
+1508DOBROPOL ERIC JEAN 11958 71978071995041249910473.33 2MTECHN
+9616AIGUELONGUE JEAN PIERRE 11958 71979101994051783213884.92 2MTECHN
+5207GANDOUX PATRICK 11958 11980081985112256617145.24 2MSANS
+5107FABRON CLAUDE 21959101982021981021326611016.48 2MTECHN
+6821ARCADIE RAPHAEL 11959 519810119800111389 9851.34 2MSANS
+9011TUTELLE CLAUDINE 21959 3199201199101 7803 7680.57 2.INFOR
+9117TOURISTIQUE AGNES 21959 31983111982111326611016.24 2MSANS
+2719CRECH DOMINIQUE 11959 81983101991012781920673.39 2MSANS
+9688BAUME BRIAC 11960 1198901198801 9894 8921.36 2MVENTE
+0734CIPRES DOMINIQUE MAURICE 11960 21984111993041527112374.40 2MSANS
+2402VALBOIS DOMINIQUE 11960 419880419870410407 9271.77 2MSANS
+4887ARBAUD JACQUES 11961 61985101993011168610047.18 2MSANS
+3196ARCADES YVES 11961 619910819900810024 8999.88 2MSANS
+0090SENLIS COLETTE 21961 7199610199510 8656 8147.17 2MSANS
+1096BOULOGNE AIME 11961111982061981061326611016.53 2MTECHN
+2728ABOUT CATHERINE MARIE 21961 219841019831011389 9851.55 2DSANS
+9891EXELMANS PATRICK 11961 61992061991061249910472.97 2DSANS
+4344SANTA MARYLENE 21962 91993081992011249910472.41 2DSANS
+6050PROSPER JOELLE 21962 41982051981051326611018.19 2MESP
+1207CAUSSADE MONIQUE ESTELLE 21962 91982111995011450311831.07 2DTECHN
+0864MIOLLIS FRANCOISE 21962 6199501199401 8656 8146.41 2MANGL
+0149MONVALLON BRIGITTE 21962 41983011982011326611016.29 2CSANS
+2375ALIZE CAROLE 21962 91983011982011215710280.24 2MANGL
+8618RESID CHRISTIANE 21962101996111995111202810241.90 2MSANS
+2736PAPIN GERARD 11962 51982051993091949415052.11 2MSANS
+3073JONCS PATRICK FRANCOIS 11962 919890519880510407 9270.62 2MANGL
+6231ENFER PHILIPPE 11962 61988031993041288210744.28 2CALLEM
+5886CHANAZ RENE CHARLES 11962 41992091991091839014312.94 2MSANS
+7276ORGEMONT DANIEL 11962 119830719910711131 9658.28 2MINFOR
+8307GUILLAUME BRIGITTE 21963 21986051985051288210744.10 2MALLEM
+6242ESCLIMONT MARC 11963121986071993051288210745.94 2MMARKT
+2593CARNOT JEROME HENRI 11963 719890619880610407 9271.23 2MSANS
+9494TAILLANDERIE JOSETTE 21963 41985101984101249910472.84 2MANGL
+6039BOURRIER ODILE 21963121984051992011288210745.88 2DSANS
+8408MARINE JEAN PHILIPPE 11963 419850619840610791 9502.46 2MSANS
+3121DOMAINES MICHEL 11963121983061983081326611016.74 2CALLEM
+3716LANDES DANIEL 11963 919851019841010791 9503.30 2MCOMPT
+7915LAMBALLE CHRISTIAN 11963 7199009199510 8656 8145.48 2.SANS
+9027RUGBY NICOLAS PHILIPPE 11964111993051992051249910472.12 2MSANS
+2392MAXIME FRANCIS 119641219850419840410791 9501.95 2MCOMPT
+3798BORNEL PATRICE 11964 51984061993071527112374.31 2MANGL
+8092DOURNAZAC MARIE CLAUDE 21964 71988031995091450311831.39 2DTECHN
+9707FEUILLANTS STEPHANE 11964 91985041984041288210744.95 2MSANS
+1926ESCLAPON JEAN PIERRE 11964101987071990011288210744.14 2MSANS
+9268DOCKS CATHERINE 21965 81985101984101288210745.97 2MALLEM
+6133MOULYN JEAN NOEL 11965 519950119940110024 8999.58 2MANGL
+2064GABRIELLE CLAUDE 119651119940419940110363 9232.71 2MANGL
+3515ARZENS JEAN MICHEL 11966121987061994101249910473.45 2CSANS
+1327LEVALLOIS CHRISTIAN HENRI 11966 419870119860110407 9270.96 2MALLEM
+4538VALLEE PATRICK 11966111987091986091527112373.35 2MSANS
+3705TOLLARE GUY 11966 319900219890210407 9271.28 2MANGL
+2150SUD FREDERIC 11966 819891119881110407 9271.41 2MANGL
+0295WEBER PATRICK 11966121989041994071249910471.94 2MANGL
+8904ARMAGNAC BRIGITTE 21966 21993011992011839014312.78 2MSANS
+0895ABORD CHANTAL 21966 31992021991011202810241.94 2MINFOR
+3361MONTMORENCY LUC 11967 119891219890411302 9775.47 2MANGL
+5793CHANEZ OLIVIER 11967 61989021988021539912452.16 2MTECHN
+9149AUBEPINES JEAN CLAUDE 11967 919950619920110024 8999.72 2MSANS
+6376MYANS DIDIER 11967 419900919890910024 8998.71 2CINFOR
+7437KERILIS CHRISTINE LUCIENNE 21967 219950519940510668 9426.77 2MMARKT
+5688TALLOIRES DOMINIQUE LOUISETTE 21968 81992121991121949415052.16 2MINFOR
+3037GRANADOS BRIGITTE 219681119921119911111389 9851.33 2MSANS
+0075CROUESTY PIERRE G 119681119900619890610407 9271.65 2MTECHN
+7531UCHA GERARD 11968 319880619870610407 9270.24 2MALLEM
+7464MORLET CHARLES 11968 319890419880410024 8999.79 2CVENTE
+0214BLAZY CECILE MARIE NOELLE 2196812199405199305 9894 8922.34 2MMARKT
+4596BONHEUR PHILIPPE 11969 81990011992061202810240.46 2MTECHN
+3345ERESTE ERIC 119691219900919890910024 8999.72 2CSANS
+2283MONTS PIERRE 11969 3199606199506 7803 7681.34 2CALLEM
+5377ROGER FRANCK ANTOINE 1196912199306199206 8656 8147.19 2.SANS
+2310PANORAMA JEAN PIERRE 11969 1199407199307 8656 8146.86 2MSANS
+5201LINCON ALAIN 11969 11990101989101202810240.66 2MSANS
+0199ROLAND ERIC JOSE 11969 119890719880710407 9270.29 2MSANS
+9200FABREGAS LIONEL 11970 419940219940310024 8999.90 2MTECHN
+7648PEPINIERES JEAN PAUL 11970 119920419910410024 8999.90 2MANGL
+0531FALAISE PATRICK 11970111993101994051450311831.87 2MTECHN
+0234TERTRE GILLES 11970 419940419930510363 9233.63 2CALLEM
+4680FREIGNEAUX TORBEN 119711119910519931110024 8999.31 2MANGL
+6897BRIGAND MAGGUY 21972 719910719900710024 8998.62 2MSANS
+3962VIMANEY DOMINIQUE 219721119940719930411389 9852.18 2MSANS
+3186VALLAURIS STEPHANE DENIS 119721119931219921211389 9851.57 2CSANS
+8810GRENELLE BERTRAND 11972 219931219921211389 9851.21 2CSANS
+4405ROBIAC FLORENT 11972 41992121993021420611638.14 2CTECHN
+1562PORTO DENIS 11972 31992021994011249910471.76 2CSANS
+9401VIEILLE DOMINIQUE PIERRE 11972 119940619930610363 9232.37 2CSANS
+6240ROMARINS MAURICE 11973 61993031994011202810241.12 2CTECHN
+7717INDRE GEORGES 11973 7199306199201 9597 8728.41 2CTECHN
+4476AUGUSTIN CHRISTIAN 11974 419941119931110668 9425.55 2CSANS
+1784LOIN MARTINE 21975 3199607199507 7803 7680.09 2CSANS
+3218FLORENCE PIERRE 11977 1199606199506 7803 7681.25 2CESP
+2085HEOL GUY PAUL 11977 5199410199502 8359 7951.66 2CSANS
+0871BOUCHE MICHELE 21934 9199201199409 7803 7680.72 3.SANS
+6519INGRES CLOTILDE 2193511199202199102 8656 8146.47 3.ALLEM
+4038ADAM JANICK 21936111960061980062470018542.10 3MSANS
+5872MURES MARYSE 2193610199210199110 9085 8417.54 3VSANS
+1672LECOIN MIREILLE 21938 7199505199405 7590 7562.85 3MANGL
+7357DETRIE MARIE PIERRE 21939 61962071983032470018540.96 3MSANS
+6152LAVA MICHEL 11939 71962111991013844228045.78 3MSANS
+3943SOUPANE MARIE HELENE 21940 31967011990011932314934.18 3MANGL
+3325REVIREE ALAIN 11941 21973071992011403411521.25 3MANGL
+2673HENNER LILIANE 21942 6199501199401 8656 8145.91 3CSANS
+1755THEBAIDE MICHELE 21942 71966061995011834514275.86 3CSANS
+7360DUCHEMIN VALERIE 21942 61970051991091932314933.73 3MANGL
+1599CHALETS ALAIN 11943121988031993011215710279.23 3MSANS
+9946AULNAY LAURENT 11945111972061995072470018540.87 3MVENTE
+2412GILLES ANNE 21946101969081990012137316330.83 3MSANS
+0843EPICEAS COLETTE 21947111971011994051834514274.26 3MANGL
+6293TERRAS ERIC 11949 41976021992012470018542.13 3MSANS
+0336AMOUR MARC 11949 31978081993121471512024.17 3MSANS
+5125BASTIDES GUY 11949 91975091990011684813422.12 3DESP
+7428LEGENDRE ELISABETH 21950 91979121978121249910472.15 3MSANS
+5015GERARD VALERIE 2195112199307199207 8656 8146.11 3.ALLEM
+4029SUISSE HERVE 11951 41970101990101932314935.08 3MTECHN
+3802BARTHELEMY JEAN 11951 11976051994041471512025.77 3MESP
+1871FELIX JEAN CLAUDE 11951 419811119870411131 9657.08 3SSANS
+0467MALMAISON HENRI 11952 81976041992011578112722.66 3MANGL
+4342PEYRE ANDRE 11953 519820419810411389 9851.64 3MESP
+4047DORDOGNE GERARD 11954 11984081993011471512025.01 3MANGL
+0870CHARBONNIERE HERVE MICHEL 119541219860119850111131 9658.11 3MANGL
+5403CLEMENT RICHARD 1195511198103198910 2079 1.27 3MANGL
+2799LOUBET HENRI 11955111980051995041249910473.33 3MANGL
+8532LEDRU GERARD 11956 41975061992081868714506.76 3MCOMPT
+6909NICOLAS SYLVIE MARIE FRANCOI21956 91977041992021471512026.04 3MALLEM
+4518CLAUDE BRIGITTE 21956 21978041992021471512025.74 3CSANS
+9477MERE MARIE LAURE 2195612199201199101 7803 7679.73 3.SANS
+5508BATHIE PAUL 11957 31979051995041249910473.65 3MSANS
+8413MARRONIERS ROBERT 11957111979121992011471512025.46 3MANGL
+4314MAUGARNY VERONIQUE 21958 11978071992121471512026.22 3MTECHN
+4329MAREUIL JEAN PAUL 11958 21981051994011471512025.56 3MVENTE
+8123CANADA FRANCOIS 119581219940719930711389 9852.20 3MCOMPT
+8870NEUVE JEAN FRANCOIS 11959 81978071992091471512025.02 3MTECHN
+8119RAMPART PIERRE 11961121981061993051527112374.76 3MALLEM
+7639ASSAS ERIC 119611019850119840111131 9657.96 3MANGL
+7720SIFFRET PATRICK 11961 719870319870410791 9502.58 3MVENTE
+5003PUYBERNARD DANIEL 119611119930419920410024 8999.03 3MSANS
+8713VAGNE FREDERIQUE JOSEE 21961111982021981091608112916.97 3MANGL
+1064CASSANYES JACQUES 11962121988061995011373711327.43 3MSANS
+6331CORMIER MIREILLE 21962 51985091984091249910472.33 3MSANS
+8037FARRERE JEAN LOUIS 11962 91987071992052529918967.97 3MVENTE
+2186JOZON CLAUDINE 21963111984091992011949415051.54 3MSANS
+6036VERQUIERES YVON 119641119880719911010407 9271.97 3MSANS
+7970FOIRAIL MARC 119641119850319840310791 9502.86 3MALLEM
+1484VICTORET NICOLE 21965 5199610199510 8359 7952.10 3MANGL
+7412FLERS GUY 11965 219880219870210407 9270.06 3MSANS
+3857DENIS BRIGITTE 21965 91993091992011202810240.55 3MESP
+5856ILIZ THIERRY 119651119860519850510791 9503.61 3CALLEM
+0008CESAR ERIC 11966 91987081986081249910472.87 3CANGL
+8742MONTANT CATHERINE 21966 91993101992011202810240.28 3MALLEM
+9552LACOMBE FRANCK 11966 51989021991121450311832.47 3MSANS
+4423ROC CLAUDE 119671019931119930110024 8998.17 3MSANS
+2631MORIZET SERGE 11967 51988041992051450311830.88 3CSANS
+2963BEAUMONT PASCAL 11968 2199306199301 9597 8728.13 3MINFOR
+1467OTTO JEAN 11968111991111990111467711987.67 3MANGL
+9461BARREAU PHILIPPE 11968 11990011989011202810240.08 3MSANS
+8108ARCANG PATRICK 11969 51990021989021381911365.41 3MSANS
+7093HERAULTS DANIEL 1196911199408199308 9597 8728.82 3CSANS
+3952LEDOUX VERONIQUE 21969 919910919900911389 9851.84 3MSANS
+7563SUPER THI VIET 11971 21990081994011249910473.67 3MSANS
+4441MIRAPOL DOMINIQUE 21971 819930619920611389 9851.34 3MSANS
+2892LOTS THIERRY 11971 2199303199510 9597 8728.23 3MSANS
+5535HOULGATES PASCAL 11971 6199607199507 7803 7681.13 3CSANS
+2838PLAGE JEAN LUC ANDRE 11972 11992071991071420611638.68 3MTECHN
+9036MERCEDES YVES 11972 51993101992101420611639.01 3CSANS
+2537MONTSOURIS PATRICK EMILE 1197211199505199505 9597 8727.87 3MALLEM
+0228AUBERTS JACQUES YVES 11972 6199404199201 9597 8729.43 3CVENTE
+5626VERNEDE ROGER CL 11973 41994101993091450311831.06 3CSANS
+6525AVES SYLVIANE 21973 2199204199104 8656 8145.93 3.TECHN
+9781OLLIER ANNE MARIE 21973 619931119921111389 9850.97 3CSANS
+1379ROSTAND PIERRE 11974 4199507199504 9126 8457.54 3MSANS
+5204CRAVANT DOMINIQUE 11974 1199601199501 7803 7681.40 3CSANS
+6619HORIZON JEAN MICHEL 1197410199606199506 7803 7681.44 3CANGL
+5826VALBONNE ANTOINE 11974 71993091994081202810240.77 3CMICRO
+0256GRANG DOMINIQUE 21974 5199508199408 7590 7563.95 3MTECHN
+4230FAISANDERIE ANNE MARIE 21974 819930819920811389 9852.69 3MALLEM
+4212ORME HERVE 11976 8199606199506 7803 7681.44 3CSANS
+6753PINTADES HERVE 11977 9199608199508 7803 7679.99 3CANGL
+7715VAILLANT CHANTAL 2193111198801199302 7677 7603.76 4.ALLEM
+8632CLAIRETTES JACQUES 11935 51964081994011557012566.54 4MSANS
+3535CASANOVA THIERRY 11936 91963081995112022115555.22 4MESP
+1574AVENUE PIERRE ALAIN 11936 719890119920811131 9657.20 4MINFOR
+4591VINCIN NICOLE 21939121966071993012137316332.68 4CSANS
+3788POTERIE MICHEL 11939 81972061990011403411520.75 4MALLEM
+0118BRETHON GUY 11939 419871119950310407 9270.03 4MSANS
+2869LIMERGUE BRIGITTE 21939 91963041991072273717260.91 4CANGL
+8403BOSQUET FABRICE 11939 81966071990011433211714.79 4MSANS
+3617VATA JEAN 119401019930819910110407 9270.62 4MSANS
+4772SAENS BERNARD 11940 719900519890510024 8999.04 4MSANS
+3869MARIUS MIREILLE 21941 81961101995072137316330.70 4VESP
+3900PERDONNET PHILIPPE 11941 119921219911210407 9270.44 4MSANS
+7958CARDINALE ROSELYNE 21942 21963101992072137316331.25 4MSANS
+9381LOTOS CAROLINE FREDERIQUE 21942 11968011990011932314934.90 4MESP
+1020FLORIDE KATIA 21942 71972071993111834514276.10 4DSANS
+9843MEON GERARD 11943 41973101991011621213032.20 4MSANS
+0204GAILLARD CHRISTIANE 21944 71964071993012137316331.82 4MSANS
+1733VUE CATHERINE 21944 61963111992072137316331.78 4MVENTE
+6248AYGUESVIVES MICHELE 21944 41970071991071684813421.33 4MANGL
+4598FEUILLERAIE JEAN MARIE 11945 219890119910610791 9502.23 4CSANS
+3077SAPIAC ALAIN 11945 71971111991012470018542.09 4MANGL
+2431RENAUDES NADINE 21946 61968111990012137316330.56 4CSANS
+1120SATUR JOSIANE 21946 51969031990012137316331.03 4CSANS
+0145ITALIENS EDITH 21946 81968111984062470018540.30 4CSANS
+4624COURDIMANCHE JEAN LOUIS 11947 81968121989102273717259.95 4MTECHN
+8599LONGCHAMP JOSETTE 21948 7199201199101 7803 7679.49 4.ANGL
+3219SERNAGLIA FREDERIQUE 21948 51969031968031326611016.35 4MMICRO
+7793ANATOLE ERIC 11948 519910119941010024 8998.50 4MSANS
+4753VENT JEAN MARC 11948 61970011988012273717261.76 4MSANS
+5892CHARLEROI MARTINE 21949101968111992091834514275.70 4DSANS
+8687GOELETTES JEAN LOUIS 11950 91975041995011578112721.71 4MSANS
+5669ADRIENNE CATHERINE 21950 61972101991072017815517.59 4CESP
+2743CLAIR MARC 11950 11978031994011369311286.26 4MANGL
+1234DAMPIERRE MICHELLE 21950111969081995042334017608.88 4MSANS
+9403LECLERC CLAIRE 21951 71971071991042137316331.51 4MSANS
+0934ENCLAVE JEAN CLAUDE 11951 21981051990041621213033.38 4MALLEM
+9416BLEU JEAN CLAUDE 11951 21976051991011369311286.78 4MSANS
+9196PYANDIERE MARIE NOELLE 21951 41982021981021326611016.03 4MSANS
+3568MOISSAN JEAN PAUL 11952111976071991071578112723.29 4MALLEM
+7750TONNELIERS MARIE 21952111973011993071471512024.35 4MINFOR
+2095MARS BRIGITTE 21952 81984011992011288210744.49 4DMARKT
+4777GORBIO MARIE DENISE 21953 61973061991011578112723.74 4MALLEM
+8945VASSY MICHELLE 21953 11973051993041471512025.88 4MSANS
+3712MAURUCHES PHILIPPE 11953 819860119850111131 9658.99 4MALLEM
+6740EUZKADI JOEL 11953 11973031990071578112722.87 4MANGL
+7024BRUSC EVELYNE 21953 71976041991121578112723.36 4MMICRO
+8219INFIRMERIE JEAN PAUL 11954 91980051990101608112916.59 4MANGL
+5540BRUAND DANIEL 11954 51974031993031684813422.75 4MANGL
+7460MATISSE MARGIE 21954 61975041991101578112723.42 4MANGL
+2655THEZAN MONIQUE 21954 91975081991091578112722.28 4MSANS
+9672BORON ERIC 11955 51975081992102017815516.74 4MSANS
+8145BOULETS DOMINIQUE 21956 1199105199005 9894 8921.63 4MSANS
+3110CERF VALERIE 21956111993081992011249910472.37 4MTECHN
+7652BAY PATRICK 11956 71976071992011578112722.47 4MCOMPT
+4074FRANK DANIELLE 21957101992111991011202810241.17 4MSANS
+4741ESCOUTADOU FRANCOISE MARIE 21957 2198901198505 8656 8145.06 4.ANGL
+0933SOUCARRADE BEATRICE 21957 5199312199212 9894 8922.21 4MALLEM
+7852CAILLOTS CLAUDINE 21958 71978091992091471512025.14 4MSANS
+5344THEODORE CHRISTINE 2195811199310199210 7803 7680.44 4.SANS
+8813MILLIERE BEATRICE FRANCOISE 21959 51981071980071202810241.36 4MSANS
+5426NATIONS ISABELLE 21959101981111980111463211946.14 4MSANS
+4656TILLET FRANCOISE 21960 71983041982041608112916.59 4CANGL
+8524ALLIER ANNE MARIE 21960 1199209199109 8656 8146.28 4.SANS
+0380MARAINVILLERS NATHALIE 21961 2199410199310 8656 8147.15 4CSANS
+1404FAISANS ERIC 119611219830319830811131 9658.86 4CSANS
+6501MERICOURT FRANCOISE 21961 519951119941110668 9426.09 4MANGL
+4607CHAPPEL CHANTAL 21961 41982091981091330611017.46 4MANGL
+1959PEY JOSIANE 21961111982111981111608112916.95 4MALLEM
+8843TIMSIT HUBERT 11961 61982041988011326611017.25 4MSANS
+1688BENOITE JEAN CLAUDE 11961 91987071992121249910472.51 4MTECHN
+5712FOUENT VINCENT EMILE 11961101983071982071326611016.03 4MSANS
+7184FRANCE RAYMOND 11961 419880319870310024 8998.35 4MINFOR
+5235HAUTS YANNICK CLAUDE 11961 819871019861010791 9503.90 4MVENTE
+6310PEYRIGOUE PATRICK 11962 81982031989071326611016.78 4MALLEM
+5397COURCELLES MIREILLE 21962 9198810198505 8656 8145.89 4.SANS
+4469PASSELEU CHRISTIANE 21962 41984011983011381911366.72 4MANGL
+8234MAIAUX BRIGITTE 21963111991071991011249910473.22 4DANGL
+9770JARD MINELLE ODILE 21963 91988111987111249910471.84 4MSANS
+5324CERF CLAUDE 11964 419870719920510791 9503.34 4MSANS
+9061ROMAGUE MARTINE 21964 71983111982111326611016.12 4CANGL
+8920PORQUIER DANIELLE 21964 41987021986021527112374.66 4CALLEM
+7098CHATENAY ANDRE 119641119841019831010791 9503.94 4CVENTE
+8128PRECONIL YVES 11965 71988021987031527112373.94 4MSANS
+6719KEFYSSIA CHANTAL 21965 319940619920111389 9851.03 4MSANS
+6244MANGEON MICHELINE 21965 8199402199304 7803 7680.34 4MINFOR
+3461LUCIA CHRISTIANE 2196610199308199208 8656 8145.56 4.ANGL
+9241ROTOURS PIERRE 119661119900819890810024 8999.13 4CSANS
+6268TOURING GENEVIEVE 21966 1199403199303 8359 7952.46 4MSANS
+4829COLLES EMILE 11966 419910719900710024 8998.31 4MESP
+0750CADET YVES 11966 919860419850410791 9503.79 4MTECHN
+0057ESCALE THIERRY 11967 71989021988021450311831.93 4MSANS
+9924CHINON BRIGITTE 21968 8199207199107 8656 8146.65 4.SANS
+7821AILES CECILE 21968 41991051992111202810239.81 4CSANS
+7114LANCIER WILMA 21968111994031992011202810241.07 4CESP
+4765TILLEULS JEAN 11969 51992041994011249910472.59 4MINFOR
+4429EDOUARD FRANCOIS 11969 319890419921010407 9272.09 4MINFOR
+1431GIORDAN SONIA 21970 5199201199101 8656 8145.51 4MANGL
+9920BOUIN CATHERINE 21970 919900419890410876 9502.28 4MSANS
+5522AURELIA MARIE CHRISTINE 21971 31993031993051267210590.24 4MSANS
+7479TRINITE NOEL 11971 3199604199504 7803 7680.36 4MSANS
+3516GASPARDES KUMIKO 21971 41993061992061267210590.05 4MSANS
+5784BOITE REMY 11972 819910619900610024 8998.26 4CANGL
+1143MACORNAY ROGER 11972 6199212199112 9597 8728.58 4MSANS
+0244FEUQUIERES CLAUDINE 21972 719921019911011389 9851.57 4MSANS
+0161LONGAGES FREDERIC 11973 119920819910811389 9852.02 4MSANS
+5206TISSOUS ELIANE 2197311199405199305 8359 7951.97 4CESP
+6992FOUGERAIE JEAN YVES 119731119931219921211389 9852.45 4CSANS
+3522REST ANNICK 21974 9199306199206 7803 7680.08 4.TECHN
+9573FONTAINE CATHERINE JEANNE 21975 2199407199307 7803 7679.87 4CSANS
+5048HAUTES JEAN RENE 11975 619961219951211389 9850.74 4CSANS
+1152QUARTIER MARTINE 21975 6199408199308 9894 8921.41 4CSANS
+0049LAUNAY NOUARA 21976 6199607199507 7803 7679.85 4CSANS
+4557ROBARESSES HELENE 2197611199507199407 7803 7680.53 4CANGL
+4244GEMEAUX DANIELE 21977 9199608199508 7803 7679.33 4CALLEM
+0257PUNAAVIA GUY 11935 219930119920110407 9271.82 5MSANS
+4173ALPINS FRANCOIS 1193510198811198505 8827 8263.65 5.SANS
+5358CHEVRY JEAN DENIS 11936 9199503199201 9085 8418.03 5MCOMPT
+4903WILSON SERGE 119361119890119930210791 9503.49 5CMANAG
+2411BINEAU RENE 11937 41970081991011433211713.71 5MSANS
+5097OURS MADELEINE 2193910198810198506 8656 8145.20 5.SANS
+1099VILLAINE YAN CLAUDE 11939101968061990011433211713.58 5MINFOR
+0060ATTAMRK CORINNE 21939 41962061983102470018542.06 5MSANS
+7199LECONTE EDMOND 11940 51972061995012201216757.94 5MSANS
+7870PIA PHILIPPE 11940 71967071991101433211715.00 5MSANS
+4990ROSSAYS SERGE 11940 21963091988012273717260.31 5MSANS
+3381BUSSEROLLES ISABELLE 21940 8199505199201 9085 8418.09 5DSANS
+0099RAHAL PHILIPPE 11940 11961051984062470018541.16 5MSANS
+5909TOURRETTES LILIANE 21940 91961101983102470018540.93 5MTECHN
+8941BEZIERS CLAUDE 21941 11961051992102137316332.72 5MSANS
+7223CHARMETTES MARCO 11941 41972091991011433211714.06 5MSANS
+8980LARMINAT FRANCOISE 21941 4198910198512 8656 8146.16 5.INFOR
+1164PROVENCAL NICOLE ALIX 21942 91968101991072273717260.89 5CSANS
+7010DELOISON SYLVIA 21942 51968121991101932314934.72 5MSANS
+3304ROSES MARIE THERESE 21942 81968091993111834514275.11 5MSANS
+7469BUTTE MARIE FRANCE 21943 21962121991072273717261.13 5MSANS
+8184VANNES MARC 11943 11971051994011621213032.96 5MSANS
+1647CLAIRES CEDRIC 11944121969011991011433211713.67 5MTECHN
+0926BEACH CLAUDE 21944 41970041991101932314934.45 5MANGL
+3781COULANGES CHRISTINE 21944 61965021992091932314934.21 5VSANS
+7004GUILBAUD MARTINE 21944 41966061995042137316330.61 5MSANS
+5511CAPUCINS JACQUELINE 21944 41965121990082470018540.74 5MSANS
+0535OLLIERES MARIE JOSE 21944111964071993072137316331.95 5DALLEM
+6700DOLET FREDERIC 11944 91973041992011403411521.64 5MSANS
+9005SUCE LYDIE 21945111977061992011471512025.50 5MTECHN
+2933PEGOMAS MONIQUE 21945 81982061981061326611016.62 5VALLEM
+1543GATTIERES MICHEL 11945 81970091991041433211715.59 5MSANS
+0725AUBIERS PHILIPPE 11945 91976051993011369311286.75 5MANGL
+2098ENTREES ANNY 21945101968061994102666519899.56 5MSANS
+8761WAGRAM JEAN FRANCOIS 11946 21971101988061932314935.35 5MTECHN
+2003PAULINE AGNES 21947 2199303199203 8656 8146.59 5.SANS
+1732RELAIS GILLES 11948 31970031993111834514274.96 5MSANS
+6970THIERS MICHEL 11948 71970101994071834514274.42 5MALLEM
+4216HUGO FRANK 11948 21971031991012990722149.47 5DALLEM
+6627ABBAYE GERALD 11948 619810319800311389 9850.65 5MANGL
+5230DUNANT PHILIPPE 11948121983101993041326611016.87 5MANGL
+2460POINTE DENIS 11949 51975081992011578112722.47 5MANGL
+7858ARBONNE FRANCIS 11949 71974081992012470018541.70 5MSANS
+3903CHAMPAUBERT PIERRE J 11949 41976101992011369311286.24 5MANGL
+8857DIVE MYRIAM 2195012199311199211 8656 8146.88 5.SANS
+0497KARINE JEANNINE 21950 5199311199211 7677 7604.39 5.SANS
+3876SANGUINAIRES FLORENCE 21950111970021991091834514275.86 5MTECHN
+3265PAVILLON PHILIPPE OLIVIER 11950111970041987102470018542.13 5MSANS
+2421VERDIER MARTINE 21950 41970071994031834514274.80 5MANGL
+8685PROVENCALES JEAN MICHEL 11950 11978061992121471512026.06 5CANGL
+6880CHARONNE GISELLE 21950 21971031994071834514275.47 5MCOMPT
+3995BELLEVUE CHANTAL 21950101977011992011471512024.48 5MSANS
+1936MANHASSET JULES 11951 31970101991021834514276.22 5MVENTE
+8771MAURIN JEAN MICHEL 119511119930819910110791 9503.74 5CSANS
+8918MORON DANIEL 11951101971031986072470018541.02 5MCOMPT
+7486HAUTACAM JEAN PIERRE 11951 11979061992011249910472.78 5MSANS
+8979PARE LUCIEN 11952 41973071991041578112723.72 5MSANS
+8316MOUSLIM PASCAL 11952 31972051990071621213032.39 5CSANS
+0252AUNES CLAUDINE 21952 71972051992111684813421.69 5MALLEM
+4525CHAVONNES FRANCOIS 11952 31976111992071578112721.79 5CANGL
+2153CROULEBARBE JEAN JACQUES 11952111972121994011684813423.34 5MSANS
+4523PITARD JACQUES 11952 91973081990123417325097.24 5MSANS
+0877MILLION CLAUDE 11952 31974121992012017815516.65 5MALLEM
+0553CRABERE HERVE 11953 91979051992121471512025.97 5DANGL
+4593COLLINES FRANCOISE 21953 81973051994031911114779.59 5CSANS
+5525MILLY JEAN HUGUES 11953 11976091991012022115555.39 5MSANS
+8419CREMIEUX CHRISTIAN 11953 71972071991072137316332.68 5MSANS
+5466CHENEY JOSE 11953101977031994011578112723.31 5MALLEM
+8647MOTHE DOMINIQUE 11953 21984071992051326611017.10 5MVENTE
+7550BORDEAUX GILBERT 11953 8199006198709 8656 8146.61 5.VENTE
+5317SIMAREGRE MARION 21954 21975081993071608112916.88 5MSANS
+7895BEAULIEU ALAIN 11954 41982031994011471512025.55 5MSANS
+7110BRUNES MICHELINE 21954 91977011994011471512024.69 5MSANS
+9833MOULINS FLORENCE 21954 3197505197405 8530 8066.58 5MSANS
+9346RIVIERE BRIAN 11955 31980081992011249910473.42 5MESP
+3155ALAUDA CATHERINE 21955 51977051993071471512025.55 5MSANS
+4303MARJORIS PATRICK 11955121980041992121369311286.96 5MSANS
+8955LACRETELLE ANNE 21955 419960719950710668 9426.57 5DSANS
+0292ENCLOS CHANTAL 21956101977071977041834514275.97 5MVENTE
+7044ENGHIEN GERARD 11956 11978061992041369311286.93 5DESP
+3352LOZERE SERGE 11957 81983021991092529918968.46 5MANGL
+7626HENIN PHILIPPE 11957101986081993011168610046.99 5MALLEM
+1198MARTYRS WILLIAM RENE 11957121977031991052043315671.24 5CSANS
+4601CEYZERIEU PHILIPPE 11958101979121987111684813421.72 5MSANS
+6850BEAUSOLEIL BRIGITTE 21958 81981031986021527112374.79 5MANGL
+0505CLAPIERS MARIE CLAUDE 2195810199403199303 9126 8456.57 5MSANS
+6127MONTEE CHRISTINE 21958 31980091994071471512024.15 5MSANS
+4080FOCH SERGE 11959 319810319810511131 9658.24 5MSANS
+8151ECUREUIL OLIVIER 11959 21989071996012815620945.31 5.TECHN
+2121BOURDAILLERIE SIMONE 21959 11979071993041471512024.56 5MSANS
+5078FERNAND ANNE MARIE 2195912199110198510 8656 8146.61 5.SANS
+2667BERGERE PIERRE 11959 51978061985041684813422.48 5MANGL
+2407SOLEIL NADIA INGRID 21959 61989061988061249910472.73 5MTECHN
+2235VILLEMARECHAL GERARD 11959 61983061989121326611017.72 5CINFOR
+0110ANDRE NADIA 21959 319930519920510363 9233.61 5MANGL
+4644AVIGNON CHARLES 11959 519830319920611131 9657.56 5MANGL
+7070MONGE HONORINE 21960111980051985061608112916.23 5MSANS
+8043EGAL LIVIA 21960 11985101984101288210744.85 5CTECHN
+5249THEODULE REMY 11960 41987051991041949415052.11 5MALLEM
+7737CLUB ISABELLE ANDREE 21960111980061994011471512025.77 5MANGL
+3853DZOANH CHRISTOPHE 11960 61988101994041288210744.58 5MANGL
+2653MARSACQ CLAUDE 11960 31985111984112256617146.01 5MTECHN
+6452POSTFACH ALAIN 11961 31985081984081288210744.98 5MTECHN
+0681LOGE PASCAL 11961 11985031991021527112372.86 5MSANS
+9537JEAN MICHEL 11961 31988061991121249910471.84 5MVENTE
+6327NOUES ANTOINE 11961121982101981101608112916.14 5MSANS
+6682VATONNE BRIGITTE 21961 71982011981011608112916.65 5MSANS
+9847GAMBADES JOCELYNE 21961101982011981011288210744.17 5MANGL
+6375GAUTIER CLAUDUNE 21961101984071994011450311832.63 5CINFOR
+6651RIVA PIERRE 11961 919830619820611131 9657.62 5MSANS
+1485REUILLY JOSETTE 2196212198901198603 8656 8146.41 5.ALLEM
+9501BETHUME MICHEL 11962 81985031993041326611016.65 5MSANS
+9435PROVENCE JEAN MARC ROBERT 11962 919940919920110024 8999.30 5CSANS
+6492VENDELAIS DANIEL 11963 119830419820411131 9657.66 5CINFOR
+9242REVELLATA BRUNO 11963111987071993041288210744.41 5MSANS
+4454ETANG BEATRICE 21963 31983081982081326611017.61 5MESP
+7265AMPHYTRION JOEL 11963 31990091993041249910472.78 5CSANS
+2299ADRIAN ALAIN 11963121988101987101288210745.70 5MESP
+8642ALE MYRIAM 219631219940719930411389 9851.34 5MALLEM
+9850BOULOURIS CHARLY BERTRAND 11963121987091986092090215980.25 5MINFOR
+3445COLOMBE SYLVIE 21964 319870619860611389 9850.74 5MSANS
+1844GUY PAUL 11964121985121994011433211715.27 5MANGL
+7102QUINQUENAIS MICHEL 11964101984091994041574112685.17 5MSANS
+1584KERVENEL JEAN CLAUDE 11964 71984061987121288210745.76 5MANGL
+2604LIBERATION FRANCOIS DANIEL 11964 819841019840810791 9503.36 5MSANS
+0403HERMITTE PHILIPPE 11964 61983051993051527112374.04 5MSANS
+5722CHATAIGNERAIE LIONEL 11964 91983121992061527112373.67 5MSANS
+3237HUYMANS MICHEL 11964 21984021990011527112372.81 5MALLEM
+5398TASSIGNY CHANTAL 21964 51988111987112090215981.24 5MSANS
+1027VRAUX PHILIPPE CLAUDE 11965 91993091993031202810240.76 5MSANS
+3198BERIN DENIS 11965 81987011994091693613499.07 5MANGL
+1036MADAME EDOUARD 11965 81986091985091288210744.77 5CSANS
+6124ABELIAS DELIA 21965 519940919930411389 9850.65 5MSANS
+1462BLANCHE MARIE FRANCE 219651219940319930311389 9852.60 5MTECHN
+0587ORMEAU GENEVIEVE 21965 51990011989011249910471.76 5CSANS
+8260BROSSAY JEAN PIERRE 11965 319931119911010407 9270.24 5MESP
+8146BROC ANDRE PAUL 11966 319940219920110024 8998.37 5MANGL
+8310HOTTINGUER ISABELLE MADELEINE 21966 1198906198512 8656 8145.98 5.INFOR
+6654TAHITI MARIE LOUISE 21966 6199501199401 9597 8729.13 5MSANS
+5470LAGARDELLE XAVIER 119661119900719890710407 9271.97 5MSANS
+8823ROUCHERETS EVELYNE 21966 61985101984101249910472.06 5MTECHN
+8327BARRELLET PASCAL 11966 41987021993121288210743.92 5MTECHN
+8879GROULT ANNE MARIE 21966 41993011991011202810241.70 5CANGL
+5928DREZERY ALAIN 11966 91989081988081539912453.11 5MSANS
+3229THOMAS SABINE DOMINIQUE 219671019940519920111389 9852.59 5MTECHN
+1136BRECHE MARIE AGNE 21967 3199601199501 9126 8456.70 5MANGL
+5446POMMERAIE GUY 11967 8199606199506 8656 8145.80 5MSANS
+0654CARAVELLES ROBERT AH 11967 81989071989021450311831.55 5MANGL
+0015TAVANNES LOAN 21967 419930619920610024 8999.72 5MINFOR
+8197NETTER GILBERT 11967 61987051994041249910472.06 5MSANS
+5236PASSERO MARC 11967 8199309199209 9085 8418.26 5CSANS
+3054WILSON GERARD 11967 119870919860910407 9272.09 5MSANS
+9725GUILHEM DANIEL 11967111990021989021539912451.67 5MSANS
+6092CLEME MARIE CHANTAL 21968 3199201199101 8656 8146.46 5.SANS
+2456HOLBERN FREDERIC 11968 2198902198802 7207 7448.96 5.ANGL
+6716VILLERSEXEL TANGI FRANCOIS 11968 2199306199206 9597 8727.78 5MSANS
+6119RAVAS ANNE MARIE 21968 6199502199402 8656 8146.19 5MSANS
+2294VILLAGE SYLVAINE 21968 91992041991041202810240.68 5MINFOR
+8140CAMIN JEAN MARCEL 11968 51988121993121249910473.45 5CANGL
+7302MERIDIEN MAURICE 11968 219880519910210407 9271.32 5MANGL
+7914JOLI MAURICE 11969 31989041992121249910472.06 5MANGL
+2148ESPAGNE BRUNO 11969 819920319910310024 8999.36 5MSANS
+1823BOUCICAULT NICOLE 21969 519941019930411389 9851.82 5MSANS
+9657MAGESCQ EMMANUEL 119691119920519941010024 8999.97 5CSANS
+9896MIO JEAN 11969 11991101990101330611017.19 5MTECHN
+8478SOURCE SIDNEY 11969 61989041988101249910473.49 5LSANS
+6336HASARD ERIC 11969 4199305199205 9597 8729.63 5CANGL
+5471LEVAINVILLE LAURENT 1197011199301199201 9597 8729.97 5MVENTE
+5314HALLES JACQUES 11970121991101990101467711985.86 5MSANS
+1685NORMANDIE MICHEL 11970 319900719890710024 8999.40 5CANGL
+6830LAOUADIE GWENAELLE 21970 2199608199508 8359 7953.65 5MTECHN
+1727CAUVETS MARTIAL 11970 7199311199409 9597 8728.99 5CSANS
+5830BRIOLLAY JEAN PAUL 11970 21991011994011249910473.24 5MINFOR
+8526CAMPION FRANCOISE 21970101991111990111202810240.91 5MANGL
+6622TANANARIVE GILBERT 11971 51992071992091330611016.75 5CSANS
+7788JOCELYN CHRISTIAN 11971 6199302199202 9597 8728.05 5CESP
+2781SAFRENIER PIERRE 11971 8199301199201 9597 8729.63 5CSANS
+6277PAULEL HERVE 1197112199403199201 9597 8729.54 5MANGL
+6618NOTRE PIERRE 11972 519930919920910363 9232.44 5MSANS
+6648SAKAKINI BERNARD 11972 319920419910911389 9851.24 5MANGL
+8593JULLIEN JEAN PIERRE 11972 8199604199504 9894 8922.45 5CVENTE
+8229CHANTELOUP CHANTAL 21972 11992101991101420611637.21 5MSANS
+8361OUCHES FRANOISE 21972 4199508199408 7803 7680.80 5CSANS
+3075LUMIO JEAN LUC 11972 41991111994011249910472.52 5LSANS
+5865FERROLLES MARIE CECILE 2197210199403199303 7803 7680.27 5MCOMPT
+0134SEVRES DOMINIQUE 11973 5199507199407 7803 7679.72 5CTECHN
+4254HENIN SERGE 11973 3199409199510 9597 8728.17 5CSANS
+8695AMBASSADE MARIE BEATRICE 21973 8199605199505 7803 7680.62 5CALLEM
+4411DAVOUST FRANCIS ALAIN 11973 719930419940711389 9851.73 5CCOMPT
+3332COUST JEAN PIERRE FLORENT 11973 2199507199407 7803 7679.46 5CSANS
+4946SAUVAGE PATRICIA 21974 5199604199504 7803 7680.77 5MSANS
+1918BABY DOMINIQUE 21974 519941019950810668 9425.45 5MINFOR
+6316CORENTIN MARTINE RENEE 21974 119930919920911389 9851.84 5CANGL
+3700PROSPER MARTINE 21974 4199508199408 7464 7524.42 5CANGL
+2405COUDUN WALID 1197411199605199505 7803 7679.55 5CALLEM
+7545DELEY MARTINE 21975 2199606199506 7803 7679.91 5MSANS
+3372HARRETCHEVERRIA FRANCISCO 11975 3199506199406 9894 8921.49 5CALLEM
+4733SIMON CATHERINE 21975 9199608199508 7464 7525.73 5CESP
+3419LAVIROTTE MICHELE 2197510199507199407 7803 7679.76 5CALLEM
+3469VASNIER CORINNE 21976 6199607199507 7803 7679.73 5CSANS
+7472VERLAI JEAN MARIE 11977 3199606199506 7803 7680.84 5CSANS
+5601PEYNIBLOU ROBERT 11977 1199606199506 7803 7680.44 5CANGL
+3668NARCISSES MARIE FRANCE 21977 9199606199506 7803 7681.07 5CSANS
+9202GARE BRIGITTE MARIE MADEL21977 3199507199407 7803 7679.58 5CTECHN
+4110KEROUAL FABRICE 1197711199607199507 7803 7679.54 5CSANS
+3531OUCHE ALAIN 11935 31965081995011557012568.13 6MSANS
+3428DAUPHINS NOUNA 21935 81967011990071578112723.50 6MSANS
+8594BAIE JACQUES 11936 51962021994033844228045.97 6MALLEM
+1267ORGE GERARD 11938 41964041994011557012567.81 6MSANS
+7513PINSONS PATRICK 11938 41963071995082947921839.78 6MSANS
+4234DORMANT CHANTAL 21939 71970011991011932314934.66 6CINFOR
+4904PANORAMIC LAURENCE 21940 9198901198610 8656 8146.50 6.SANS
+3727GUYNEMER VERONIQUE 21940 51972051989101621213032.21 6CSANS
+8364COUDERC ANNICK 21941111973101990071621213033.61 6CANGL
+1743VALLEES GERARD 11941 71970041995062470018540.61 6MANGL
+5058LIBERATION THIERRY 11941111973101991011403411521.37 6MSANS
+8875BRAILLE CLAUDE 11941 71974041989101834514274.74 6MSANS
+5325HESPERIDES JACQUES 11942 81963041991072273717260.64 6MANGL
+9083PONS JEANNE 21942111972101994041894014662.64 6MSANS
+3082SIROIS JEAN 11942 11965111989071663613342.67 6MANGL
+0164ENCRABE DOMINIQUE MARIE 21942 8198810198410 8656 8145.03 6.ANGL
+5264PARLY MARIE HELENE 21943 61963031993032922321685.16 6MANGL
+3102GRDE GERARD 11943 11966061991012470018540.24 6MANGL
+6225GRAVAS GILBERT 119431219750819881211389 9852.47 6MINFOR
+0486TOCQUEVILLE MICHEL 11943 71965061990012470018541.72 6MSANS
+6103DAUDIN MICHEL 11943 31971081991071433211713.97 6MANGL
+9562JULES MARYSE 21943 71962091984062470018540.75 6VANGL
+1659EVANS MARIE NOELLE 21944 21963121994072137316331.91 6MSANS
+5753SAXE BERNARD 11944 51964111985063200223582.85 6MSANS
+5551AMBROISE GILES 11944 21972081991012470018541.97 6MSANS
+2734FELICE PHILIPPE 11945 91970051991011433211715.24 6MSANS
+2627SAVIGNY ANDRE AUGUSTE 11945 71964121990052470018542.04 6MSANS
+2707VAUGIRARD HELENE 21945 8199407199307 7590 7562.63 6MSANS
+0420DARDOUNELLES DANIEL 11945 21971021990011433211714.52 6MINFOR
+0574JAURES MARC 11946101972081994011663613343.70 6MANGL
+4658BORA JEAN PIERRE 11946 31973091992011403411520.69 6MMARKT
+2852BERLIOZ PASCAL ALAIN 11946121969111991012273717260.45 6MSANS
+9183STATION BERNARD 11947 61976101991011283810705.62 6MSANS
+7225BONSECOURS ANNE 21947 51976071993111471512024.74 6DSANS
+5915ANTILLES ISABELLE 21948 61969081995101433211714.47 6MSANS
+7173GIOTERAIE ROBERT 11948 61967111986042470018541.91 6MANGL
+7940NICOT ERIC 11949121974041992011403411520.15 6MSANS
+6066CLEMENCEAU YVES 11949 31971041995012201216756.31 6MSANS
+8113VITRY DOMINIQUE 21949 7199311199211 9597 8728.28 6DVENTE
+6047TASSIGNY THIERRY 11950 41973081994122470018541.72 6MTECHN
+9002BERRAT BRUNO 11950 51977051993041369311286.29 6CANGL
+8038AUDIBERT PATRICK 11950 719770119931211389 9850.58 6MINFOR
+4759BLANCHE MAYAKO 21950 51970101991071578112723.11 6MSANS
+6982CYRANO DANIELLE 21950 51974111991071578112723.74 6CANGL
+9782BARRAGE CLAUDINE 21950 3199109198902 8656 8146.01 6.ANGL
+4552ABBADIE MONIQUE 21950 61976011991101578112722.87 6MANGL
+3112AGENT DANIELLE 21950 11993121992011202810240.31 6MESP
+4893GRANDE ISABELLE 2195012199507199301 7677 7604.70 6MINFOR
+6863FERTE MARIE JOSE 21950 71970101991012137316330.65 6MINFOR
+3487TALARIC MICHELINE 21950 21973101991071578112722.51 6MANGL
+7125FERRAYONN JEAN MICHEL SERGE 11951101977101994041249910473.47 6MVENTE
+5445RON DOMINIQUE 11951121971101991013200223582.31 6MSANS
+0070PLASCASSIER DANIEL 11951121975051991011369311286.30 6MSANS
+0688PARGAMINIERE JEAN 11951101976061993011369311286.15 6MSANS
+0032FOUR CLAUDE 11951 61975111991011684813421.95 6MCOMPT
+2442BOULOC MARIE FRANCOISE 21951 31974051986052334017609.66 6MALLEM
+3898KENNEDY YVES 11951121977101991011369311287.25 6MANGL
+8228LEFRANC JEAN LOUIS 119521219931119910110791 9502.26 6MSANS
+7142LEGER VINCENT 11952 81976051991011684813421.99 6MANGL
+0571DOMINICAINES JERZY 11952 31971101991013200223582.94 6MSANS
+0382KERHAM CATHERINE 21952 21974041991071578112722.07 6MSANS
+8150BEARN FRANCK 11952 21982091993011527112372.87 6MSANS
+7995BAUDIN DANIEL 11953 91975051994011369311287.19 6MSANS
+4159CHASSENARD JEAN BERNARD 11953 519870619860610791 9503.21 6MSANS
+3964FAREMOUTIERS LAURENCE 21953 6199311199211 8656 8145.08 6MSANS
+0454CHERBOURG ISABELLE 21953 6199610199510 7803 7679.81 6MVENTE
+5111CROIZAT CHRISTOPHE 11953111973031990071578112722.69 6MALLEM
+2014LISIERE XAVIER 11953 8198911199501 9894 8923.22 6MSANS
+9741PARIS PASCAL ELIE 11954101978081991012022115555.53 6MTECHN
+0737YERRES GERARD 11954 61976091995042529918968.84 6MTECHN
+3053TORRE PAULE 21955 91976061995101881314585.97 6MVENTE
+2120CORMEILLE JEAN LUC 11955 5199610199510 8656 8146.74 6MSANS
+1540PIJOUNIE ANGELE 21956 51977021981091834514274.87 6MANGL
+6259VICQ CATHERINE COLETTE 21956 219780719770710668 9425.82 6MSANS
+8345DAGOBERT DJARN 11956 719820919871111131 9658.24 6MINFOR
+3734GARNIER PASCAL FRANCOIS 11956101977081994042201216756.71 6MSANS
+4365ESSO ANTOINE 11956121976061994011369311288.10 6MSANS
+0262ROLLAT HERVE 11956111979081991012175716601.93 6MSANS
+7989RHONE YVES 11956 71977051994041249910472.06 6MANGL
+4035ISLIP ANNIE CLAUDE 21957 41993101992011249910472.06 6MINFOR
+8638DUNOIS JACQUES 11957 51981031995062273717260.50 6MINFOR
+6417ALBI RENE 11957111980091993121369311288.04 6MANGL
+7559MARIENTHAL MONIQUE 21958 319940919930411389 9852.59 6MSANS
+1621CAMBRIDGE ALAIN 11958 8199312199212 9597 8729.04 6MINFOR
+3476CREPY ALICE 2195811198711198611 9511 8688.22 6MSANS
+2482CHORON EVELYNE 21958 7199110199409 8656 8145.42 6.SANS
+4165BALZAC MICHEL 11958 71985121995011433211713.76 6MSANS
+9040FERDINAND CHARLES 119581219781019771011389 9852.24 6MANGL
+6858CUVRAY MARTINE 21959 71980041994011471512025.82 6MMARKT
+2723PAPARA FRANCOISE 21959 61980051994011471512025.70 6MTECHN
+1222FOSSES MONIQUE 21959 21980041993121804514081.78 6MTECHN
+8741BOULEVARD MICHEL JACQUES 11959 91979021993041471512024.62 6MTECHN
+5441ZOLA BRIGITTE 21959111979081993071471512025.82 6MINFOR
+9947ERMITA CATHERINE 21959 81982081981081326611017.38 6MSANS
+2563FLORISSANT PHILIPPE 11959 51981071989052256617145.21 6MMANAG
+3583MOLLAY JEROME 11959 81981031993051608112916.41 6MALLEM
+6466ORMES ANNIE 21961 21981081995071433211713.85 6MALLEM
+1432AGRIANT ROBERT 11961 31984031991091527112372.83 6MCOMPT
+3657NOVEMBRE SERGE 11961 51986071992101527112374.36 6MSANS
+5054FRANCK HUGUES 11961111984031983031608112915.87 6MALLEM
+7313CRESTET JEAN PIERRE 11961 41984091994091326611017.55 6MSANS
+1383ORIOL ANNE 21961 61983021982021288210744.01 6DSANS
+9235DANTE JEAN MARIE 11962 71984031993081527112374.13 6MANGL
+1924MALVEZIN MARTINE 21962111984071983071608112915.47 6MSANS
+0722MUSSE JOELLE 21962101982081981081608112916.65 6MMICRO
+2536BEAUSOLEIL DOMINIQUE 11962 219830419851210791 9502.17 6MSANS
+3286DEEPFIELD GERALD 11962 319930819921210876 9502.09 6CSANS
+9760CHAMPET MARTINE 21962121988101987101202810241.67 6DTECHN
+0459FERRAILLONS NICOLAS 11962 819850919840910791 9501.95 6MSANS
+9070ROUGUETS JEAN RENAUD 11962 919920519910510024 8999.54 6MSANS
+5609MONNEGER GEORGES 11962 61984101993051527112374.00 6MALLEM
+8699COLMAR CATHERINE 21962 21988061987061202810241.09 6MINFOR
+0121PARAY JEAN FRANCOIS 11963 219830419920311131 9658.07 6MVENTE
+9033TIMBAL FRANCOIS 11963 91991021991022529918969.60 6MSANS
+0477HORTENSE ALAIN 11963 51984021992101527112374.84 6CSANS
+9565CARRETIER JOSE 11963 81986121992121450311830.53 6MINFOR
+5210BARTHOU PASCAL 11963 319850419850511131 9657.21 6MANGL
+7365GALLAIS PATRICE 11963101984031983081288210745.79 6MSANS
+3436TADJOURAH PATRICK 11964 51984061991011288210744.35 6MTECHN
+0043HILLS JEAN MARC 11964 719840619830610791 9503.67 6SSANS
+6646ROOSEVELT FLORENCE ANNICK 21964 51989111988111249910472.39 6MESP
+5680PORSMORIC MARIE MADELEINE 21964 31983041982041326611017.97 6MANGL
+6503MONCEY JEAN PAUL 11964 319940219930110024 8998.77 6MALLEM
+9162BROSSOLETTE MARYVONNE 21964 91984111983111288210744.28 6MSANS
+5895QUATRE DANIEL 11964 31984101993011616712994.89 6MSANS
+3689ETOILE ALAIN 11965 31985051992011527112374.54 6MSANS
+6754MAXIMIN JEROME 119651119930819920811389 9851.10 6MANGL
+1818BOYARDVILLE KINEO 11965 919850519840510791 9501.83 6MALLEM
+6998HONORE PATRICIA 21965 31989051994011381911366.18 6DSANS
+0086COLLETTE SERGE 11965 41985091991121288210744.14 6MALLEM
+2914ESPAGNET ERWAN 11965 21984051983051288210744.17 6MSANS
+9284COLIBRIS JEAN JACQUES 11965 41993101992101330611018.00 6MTECHN
+5732LEUTHREAU ALAIN CHRISTIAN 11965 11987081992011450311831.39 6MSANS
+7234MARDELLE ROGER 11965 81986041993051450311832.14 6MALLEM
+2688TILLEUL FREDERIC 11965 619930719940110876 9503.04 6MSANS
+6325MIGNEAUX FRANCOIS 11965 419940119920110407 9270.44 6MANGL
+0520PIBRAC STEPHANE 11965 219850819840810791 9503.76 6MSANS
+5142HUBIES BRIGITTE NOELLE 21966 41991071990071202810241.79 6MCOMPT
+2662PEYLONG GHISLAINE 21966 9199201199101 7803 7680.36 6.SANS
+5787PRADIER PHILIPPE 11966 11987051994091693613499.34 6MALLEM
+0594PARK GILLES 11967 11989101988101249910472.52 6MSANS
+9155VIADIEU DIDIER 119671219940419920110024 8998.50 6MSANS
+9944STRASBOURG ROBERT 11967121993061992061267210590.24 6MALLEM
+0751MONTDAUPHIN CLAUDE 11967 819910319950610024 8998.80 6CANGL
+6596INFROIT STEPHEN 11967 41990011994011249910473.45 6MSANS
+2539CARTHY ANNICK 21967 41994071992011202810241.09 6MSANS
+9966ENFERT BRIGITTE 21967 919930719920711389 9851.37 6MALLEM
+8934REGNIER ANNICK 219671019950319930411389 9851.96 6MALLEM
+1654LEADER MICHEL 11967101995101994101839014312.60 6CANGL
+7794ANEMONES EVELYNE FRANCOISE 21968 11993031991011202810241.66 6MTECHN
+5095BLAIRE MARIE JOSE 21968111989101988101539912453.06 6CSANS
+3446RICHOIN JEAN PIERRE 11968121990091994121450311832.51 6MINFOR
+0591FERON CHRISTINE 21968101994031992011202810240.76 6CSANS
+6142SORBIERS ROSWITHA 21968 81993061992061267210589.60 6CSANS
+4698JEAN GERARD 11969 51989101991091249910472.96 6MTECHN
+9192SAVIGNE ALAIN 11969 31991011990011202810240.26 6MESP
+0014NANTAUX ETIENNE 11969121994061992011202810240.46 6MANGL
+6632JULLIAN FREDERIC 11969 9199102199002 8656 8145.20 6.SANS
+1053MORTIER GUY 11969111992121991121330611016.35 6CALLEM
+3261SUCHET MARIE THERESE 21969111994031992011202810239.91 6MSANS
+4711BLANCHE ALAIN 11969121992121993031267210589.55 6MCOMPT
+5942FLOREAL JEAN PAUL 11969 11990111989111381911366.49 6CVENTE
+1744ESPAGNE JEAN PAUL 11969 119900219890210024 8998.41 6MESP
+7503TOUCHARD ANNE MARIE 21970 21990111989111202810241.57 6MSANS
+2585ESTIENNES JEAN 11970111993111992111330611018.18 6MANGL
+8805CHENIER ROBERT 11970 71992071991071420611638.05 6MSANS
+7438ROSELINES SYLVIE 21970101994031992011202810240.72 6CSANS
+6724POTHONNIER MARIE JEANNE 2197011199206199106 8656 8146.42 6.SANS
+5056POINCARRE KARINE 21970111994031992011202810241.45 6MINFOR
+2915REILLE MICHEL 11970 7199602199502 9256 8533.59 6CANGL
+3988ATALANTE BRUNO YVES 1197012199402199302 9597 8728.68 6CANGL
+3548JOURDAN GUY 11970101994061992011202810240.35 6CALLEM
+1071CHAROLLES JACQUES 11970 1199306199206 9597 8728.82 6CINFOR
+7830BOCH BERNARD 11970 3199304199308 9597 8729.09 6CINFOR
+7543REIN PIERRE 11970 9199501199401 8656 8146.05 6MSANS
+0754ELANS EVELYNE 21970 5199010198910 8656 8146.07 6.SANS
+0414CHARDON HERVE 119711019931019921011389 9852.65 6MESP
+3628VILLACOUBLAY PHILIPPE 11971 919930319920311389 9851.12 6MALLEM
+0150ADDAX MICHEL 119711219940719920110024 8999.70 6CVENTE
+9176CAHEN HUGUES JEAN LUC 11971 219921019911011389 9852.24 6CSANS
+2846COOLE SERGE 11971 1199403199201 9597 8729.70 6CSANS
+2967LANCIERS KLAUS 11972 819930319920311389 9851.59 6CANGL
+5218EXELMANS JEAN JACQUES 11972 1199210199101 9597 8727.83 6MMICRO
+5276ALLEE JEAN JACQUES 11972 519931219921210363 9233.79 6MALLEM
+7650FARLED DANIEL 119721119941219931210363 9232.28 6MTECHN
+1760COURTILLE FLORENCE CLAUDE 21972 419940819920111389 9851.61 6MSANS
+0061FLORIDA CLAUDE 21972 719930519920511389 9851.24 6CANGL
+4799DUBAN REMY 11973121993011994011202810240.82 6CSANS
+5516LUZAC BERNARD 11973 5199606199506 7803 7680.27 6CMICRO
+2886KERINEL PATRICK 1197311199309199209 9126 8456.47 6CALLEM
+2318BURNOL MICHEL 11973 6199310199210 9597 8729.58 6CALLEM
+2243BASCH GEORGETTE 21973 319930919920911389 9852.41 6MSANS
+9674JAUNE SANDRINE 219731219951219941210668 9426.66 6MVENTE
+3507CAMPANULES GERALDINE 21973111994031993031267210590.14 6CTECHN
+8457ROAD MARTINE 21974 719940619930610668 9426.03 6MSANS
+5644PERROQUETS PHILIPPE ANDRE 11974 3199407199307 9126 8455.98 6MANGL
+5222PASTEUR CLAUDE 11974 3199606199304 9126 8456.34 6CTECHN
+2362GORBELLA ANNIE 21974 21994091994071450311830.61 6CANGL
+3214NELSON JEAN YVES 11974 2199412199312 8359 7953.60 6CINFOR
+2883CHEIRON MICHEL 1197511199611199511 9256 8532.96 6CSANS
+3892LENNON NICOLE 21975 3199207199307 7803 7681.02 6.INFOR
+2980ROASSAL JEAN MARC 11975 7199507199407 7803 7680.67 6CTECHN
+3954BARONNETTE FRANCOISE 2197612199610199510 7803 7680.59 6CANGL
+8734MARGUERITE ANNIE 21977 6199606199506 7803 7679.49 6CSANS
+2986ROURE CLAUDE 11977 8199608199508 7803 7679.40 6CSANS
+2812FABIEN CATHERINE SYLVIE 21930 1198905199501 8827 8264.04 7.SANS
+4059LECOURBE FREDERIQUE 21935 41961101982052470018540.38 7VSANS
+3810LACS ELIANE 21935 51961111987012273717261.07 7MANGL
+1179VIES FRANCOISE 21935 1199306199501 8656 8146.82 7.TECHN
+4536CAPUCINS JEAN PIERRE 11936 61958121993103200223584.44 7MSANS
+5665CAMOIN MARIE CLAUDE 21936 8198910198511 8656 8145.15 7.SANS
+5858BONODIERE MARIE CECILE 21937 41962071985102273717261.25 7MALLEM
+3737MAREY ANDRE 11937101973071992011403411520.80 7MALLEM
+3343ECLUSE SERGE 11939 21970081995062201216755.92 7MMARKT
+7410MONNETIER MARIE JOSEPH GEORGE 21939 51963051991072273717260.50 7MANGL
+9145ULI MARC 11940 31969061991011433211714.60 7MALLEM
+4704MANOLA MICHELLE 21941 11961081987032572519241.16 7CALLEM
+6915PLERGUER JOSE 11941 81973071992041403411521.34 7MSANS
+1437DIGUE GEORGES 11942 11973071992011403411521.70 7DSANS
+7658DOYENNES PATRICIA 21942 51964101992072273717261.07 7MSANS
+3938DOMINE OLIVIER 11943 51971041991011433211714.10 7MALLEM
+4424CORBEIL JEAN PIERRE 11943 81974051993011369311287.59 7MSANS
+7614PHILIPPE MICHEL 11943 31963091986012273717260.13 7MSANS
+3959RONCIERE MARIE CLAUDE 21943 21973071990041621213033.19 7MSANS
+9296THABAS ANITA 21944 51964071990102273717261.39 7CALLEM
+9017DELAPORTE ANNE PATRICIA 2194511199201199101 7803 7679.64 7.SANS
+3629RAMBUTEAU JEAN PIERRE 11945121972061991071433211715.51 7MANGL
+3070ETIOLLES MICHEL 11946 21975031994121471512025.95 7MANGL
+3212CYCLADES BEATRICE 21946 31966121995072017815517.38 7MSANS
+8064ARCANGUES MARIE CHRISTINE 21946 71969031984062470018542.19 7CSANS
+4281GAURBETS CHRISTIAN 11946 319890119941110791 9502.01 7MANGL
+0562BLANQUET ANNE 21946 6198810198511 8656 8145.21 7.SANS
+2853MESTE MARYSE 21946 1199301199201 9085 8417.91 7MTECHN
+4550CHEVRY CLAIRE 21947 71968041993111834514275.06 7MVENTE
+2513EUDES SABINE 21948 11970041993121834514276.18 7MTECHN
+2928HEYRAULT FRANCOIS 11948 21972041991012137316331.56 7MESP
+8846LIGNIERES JEAN CLAUDE 11948 41975061991011369311287.77 7MSANS
+0470POTEL PHILIPPE 11949 21974051991112043315669.51 7MCOMPT
+7926OUSTAOU CATHERINE 21949 31970111988042334017610.69 7CVENTE
+1398NOUVEAU MARIE PIERRE 21949111969101990012137316331.39 7CSANS
+7196THIERS PASCAL 119501019800619790611389 9852.33 7MSANS
+8076DELANNE COLETTE EMMANUELLE 21951 71971061990011621213033.43 7MSANS
+8525VOISIN PIERRE 11951 61976051991071578112723.68 7MTECHN
+7965DELESTRAINT MARIE CLAIRE 21952111973021992102017815517.91 7MALLEM
+2553CONVENTION MONIQUE 21952 91978061992121471512025.64 7MTECHN
+3819SOLEMAR ANNE 21952 81979061979021684813422.18 7CTECHN
+3540SAISSY DANIEL 11953 81980081989041684813422.77 7MALLEM
+1880GOUFFE JEAN CLAUDE 11953 71983061995011578112722.61 7MSANS
+7620MOIRAX REINE 2195310199307199207 9894 8923.11 7MSANS
+2953TOMBE PATRICIA GABRIELLE 21953 41975101991091578112723.06 7MTECHN
+5507ALEXIS JOCELYNE 21954 41975011974011288210745.40 7MSANS
+4941FONVAL MAX 11954 41978051994011369311286.83 7MSANS
+4394BRIAND JOSETTE 21954 71975051995011881314584.61 7MALLEM
+6207FOINS CLARISSE 21954121976071992011578112723.18 7CSANS
+9748CHARVE MARIE CLAUDE 21954 1199103198801 9894 8921.67 7MSANS
+9682TIPHAINE JEAN CLAUDE 11955101978091994041471512026.06 7MINFOR
+1788NAJY ALAIN 11955 11975031991071578112721.89 7MINFOR
+7956NOTAIRE FRANCOISE MADELEINE 21955 31977011981041684813422.41 7MSANS
+3415PRAIRIE PASCALE 21956 719911219901210024 8999.63 7MSANS
+9208ESCADENIERES JEAN FRANCOIS 11956 61984081993011471512026.22 7MSANS
+0945MAISON HEIKE 21956121977021992011471512024.30 7MSANS
+2271LANDE MICHEL 11957 41982021994011471512025.56 7MSANS
+3201BANGKOKNOI CATHERINE MONIQUE 21957 41978081977081834514275.41 7MANGL
+6914BANCHEREAUX JOSETTE 21957 31981051995041433211714.15 7MANGL
+8957LAFFON BENOIT 11958111977091994011621213033.05 7MSANS
+4169FOUBERT AUGUST 11959 819820519871011131 9658.59 7MANGL
+7880DIV JOELLE 21959111982041981041330611017.86 7MMICRO
+7448ORSAY SYLVIE 21959 11984111983111608112917.15 7MSANS
+6126SAUVEUR ANNICK 21961 21981051980051288210745.75 7MSANS
+7782PETRO CHRISTIANE 21961 4199004198706 8656 8145.57 7.SANS
+3730PEREIRE FABIENNE 21962 51982091981091608112916.37 7MTECHN
+4013TROUIN ETIENNE 11962 21987061993091839014312.81 7MTECHN
+1602VINCENT MICHEL 11963 51986111985112090215979.83 7MSANS
+1113AVENYE PASCALE 21963 8199407199307 7803 7680.81 7MSANS
+5901GIORDAN JACK 119641119840219830210791 9503.99 7MTECHN
+5007CASTEBELLE PATRICE 11964 41984061993121288210745.40 7MVENTE
+5706QUIOU SUMIYO 21965121986091985091288210745.79 7MSANS
+8009COULOMMIERES FRANCIS 11965 119880819870810407 9271.16 7CSANS
+4505VERGNE IRENE 21966 61991031990031381911366.63 7MSANS
+2764BALAN GABRIEL 11967 21988051992121249910472.84 7MSANS
+5527CHAMIER BLAISE 11968 81992011991011381911366.84 7MSANS
+9559OUSTALET JACQUES 11970 2199207199402 8656 8146.28 7.ANGL
+9156GUILHERMY YUKO 21972 8199208199108 7803 7679.37 7.SANS
+2342LAJARRIGE MARIE JOSE 219721219920719910710363 9233.39 7.SANS
+0358MERINDOL JEAN PIERRE 11974 81995051994071288210745.57 7CALLEM
+7473MERMOZ JACQUELINE 21931 7198810199501 8656 8147.15 8.SANS
+7635DERAIN NATHALIE 21933 4198903199501 8656 8145.62 8.COMPT
+2535TREVISE PHILIPPE 11935 61963081994122666519901.15 8MSANS
+4757GERMINY MARIE LOUISE 21935 81954091982072273717260.53 8MANGL
+5975FAVORITE MICHEL SERGE 11935121963041981052470018541.88 8MSANS
+3758CALANDRELLES YVES 11935 51968021993011663613344.05 8MVENTE
+3094TARN ANNICK 21935 61959081982052470018541.01 8MSANS
+7689COURONNE MICHEL 11936 91958091995012666519900.32 8MSANS
+0853COLLEDEBOEUF PIERRE 11936 71973031992041403411520.30 8MSANS
+5252COSSIGNY PASCAL PAUL 11936 21964121989072947921839.52 8VSANS
+2084HARET COLETTE 21937 5199605199505 7803 7680.09 8MSANS
+8285MASSENET MARIE FRANCE 21937 81963091991072273717260.26 8CANGL
+7176ROBESPIERRE PATRICK 11938111962121985072273717260.68 8MANGL
+1151QUERCIOLO ABDELAZIZ 11938 31965111995011834514274.24 8CSANS
+1464SANE CLAUDE 11938 9198801198604 8656 8145.80 8.INFOR
+6980MARTIN CHRISTIAN 11938 71972101990011433211714.42 8MSANS
+7189CUERS JEAN BAPTISTE 11939 81966071993011663613344.45 8MSANS
+7123BOISSIER MARIE JOSEE 21939 61962121990102273717260.22 8CINFOR
+7519VACHET CATHERINE 21939 11983081992011326611016.12 8MANGL
+2640BARBERAZ MARYSE 21939 61967011989091932314933.85 8MSANS
+1630GRACAY ISABELLE 21939 71966071991101932314933.67 8MSANS
+6303DOUBLE YVELINE 21939 51964071994102666519901.01 8DANGL
+8547BEAU SOPHIE 21939 71967071990071578112722.87 8VSANS
+7642ROCHEREAU ANNICK 21939 41966061994072137316330.94 8MSANS
+4913LAURICESQUE JEAN FRANCOIS 11939 619900719920710407 9271.92 8MSANS
+0289CARIBOU ELISABETH 21940 21959051992091932314934.03 8MSANS
+5838MAUPERTHUIS CLAUDE 11940111961061994053417325097.60 8MVENTE
+1874GRANGES CATHERINE 21940 819940919920110024 8999.72 8MSANS
+1504FONTENAY GERARD 11940 51963121988113200223582.58 8MSANS
+9111MONTILS DENIS 11940 31973031992071369311287.46 8MSANS
+8171VIVES FRANCOIS 11940101966071989071433211715.65 8MSANS
+5585COLLEGE REMY 11941111971121991052022115556.35 8MSANS
+6338ANSE FRANCIS 11941 11963071992041557012568.02 8MVENTE
+9521ARBOUSIERS ANNY 21941 71963031993122334017609.66 8DSANS
+5504HONORE MARC 11941101966071990011433211715.68 8MSANS
+0434ROUGET ELIANE 21941 41961051994101684813421.91 8MINFOR
+8697ROCHEFORT MARIE CLAIRE 21941 81963031991072273717259.86 8CANGL
+5183NID BRIGITTE 21941111969011992121834514274.42 8MANGL
+4171LAFFITTE ODILE 21941 6199201199101 7803 7679.41 8.ALLEM
+9168OISANS DOMINIQUE 11942 61976101992011578112723.00 8MSANS
+0577REDERSKAAI ANNE 21942 41971011994071834514275.68 8VSANS
+4455LORREZ ERIC 11942 4198810198604 8656 8146.28 8.ESP
+1456ALESIA LIONEL 11942 41963041984102470018541.64 8MANGL
+6360QUESSINE PATRICK 11942 51962111993123200223584.16 8MSANS
+9206RAVAGERS LUC 11942 71967011994121557012567.18 8MSANS
+5506ORA MARCK WITOLD 11942 81964071992012470018540.60 8MANGL
+0645MALOUET MYRIAM 21942 81962101991072273717261.18 8MSANS
+2325LONGUERAIE GUY 11943 61975031990011433211713.61 8MSANS
+6153LUNAIN LINETTE 21943 9198810198604 8656 8147.06 8.SANS
+8628ANCIENS PATRICK 11943 21963041992032739020403.89 8MVENTE
+7345FLORENT FREDERIQUE 21943 41962121987082470018541.61 8CTECHN
+9650GODARD GERARD JEAN 11943121975021993011369311286.71 8CANGL
+0047BEUNE YANNICK MARIE 11943 71962121984092470018540.80 8MSANS
+9696RAFFET CHRISTOPHE 11943121993041992041642313188.35 8.ESP
+2868MONTREAL SABINE PASQUA 2194312199401199301 8656 8146.11 8MSANS
+9052ORANGE JACQUES 11943 61972071992011433211714.46 8MANGL
+0622GEOFFROY MARTINE 21943111970091991101932314934.93 8VVENTE
+5329VILETTE GUY 11944 21967121990041433211714.75 8MSANS
+7979BORDS MAEVA 21944121964071993061834514275.97 8MSANS
+0046GREENS BRIGITTE 21944 4199009198909 8656 8147.19 8.SANS
+7861LAGACHE CAROLINE 21944121963111991072273717260.85 8MSANS
+6041MAIRIE MICHEL 11944 61969101989056400345693.48 8MSANS
+6790VAUX ARIANE ELIANE 21944 31964071993102137316332.47 8MANGL
+7706NICOLE CHRISTIAN 11945 31966041995072137316330.79 8CSANS
+3032VOIE CLAUDE 2194510199501199401 7803 7680.95 8CSANS
+4678GASCOGNE LYDIA 21945 51966121993011471512025.37 8MTECHN
+6211LEFEVRE MIREILLE 21945 61966021995042137316330.58 8MSANS
+4373CHENAIE FRANCOIS 11945 419940919930910363 9232.55 8MSANS
+6516SABLONS ROBERT 11946 91982011992101249910472.66 8CSANS
+3672CORVETTE JOELLE 21946101968111992121834514274.53 8DALLEM
+6368ALPES ANNE CLAIRE 21946 31965121993031834514276.15 8DTECHN
+9883MATRUT NICOLE 21946121973041993041471512025.11 8MSANS
+1004DUNOIS GERARD 11946111973071992121518612335.69 8MSANS
+5302REMUSAT ALAIN 11946 11975051991011578112723.33 8MSANS
+0673VASES DANIEL 11946 41969051995032666519900.02 8MINFOR
+5739COUR CLAUDE 21946 3199512199412 8656 8146.09 8MSANS
+0992EVENOS GERARD 11947 819941119920110407 9271.52 8MCOMPT
+2791BERTHELOT ANNIE 21947 51977071991101578112723.00 8CSANS
+7501GOUBET FRANCOIS PIERRE 11947121972121991071621213032.53 8MTECHN
+8208PIERRAILLES MICHELINE 21947 91971011995011834514275.91 8MSANS
+2702BLAQUE JEAN JACQUES 11947 11970061991102739020405.01 8MTECHN
+8704COLLINE JEAN PIERRE 11947 119890119880110152 9116.67 8MSANS
+4666HELLEN PIERRE 11947 61979021993041471512024.60 8MSANS
+8568AQUILON ISABELLE 21947 11969011991012137316332.22 8MANGL
+7824REVA JOSIANE 21948 6199003198608 8656 8146.61 8.SANS
+9576MONTET LIONEL 11948 61969121994072470018541.95 8MSANS
+3165HAUTS MYRIAM 21948 51970121991022022115555.81 8MANGL
+9783THOMAS MARIE PAULE 21948 51971101990072022115555.44 8CMANAG
+0892EPARGNE PIERRE 11948121973051990011684813423.29 8MSANS
+8463GIMONT MARIE LAURE 21948111970011991071834514274.30 8MSANS
+3612GLENAN ALAIN 11948 11973071990011834514276.06 8MSANS
+3989AVENIR HERVE 11948 41979071992011249910472.66 8MSANS
+1849GRASSE JEAN MARIE 11948 51974111992041403411521.97 8MSANS
+5860GRANGES MARLENE 21949121969031989011621213032.26 8MSANS
+0021ECOLES MARTIAL 11949 91981091992011249910472.37 8MSANS
+5620BLANC MARIE SABINE 21949 1199107198704 8656 8146.01 8.SANS
+9485MONT PHILIPPE 1194911198704198604 8656 8146.68 8.SANS
+5814TERMES YVES 11949 41978021993121369311286.03 8MSANS
+7863YESILKOY DOMINIQUE 11949 71969031990012137316330.92 8MANGL
+0579HILARION ETIENNE 11949 31968121989021932314934.09 8MSANS
+4379FORGERONS JEAN FRANCOIS 11949 11977041993011369311287.16 8CANGL
+3965BOISGELOUP ANNE ANDREE 21949 81971011984082470018542.24 8MINFOR
+8322FLAYAT RICHARD 11949 119800919860911389 9851.64 8MSANS
+3220PLASCASSIER CATHERINE 21950 91974051991071578112721.53 8CSANS
+2062NOIRAY GUY 11950 31993111992111642313189.25 8.SANS
+1017COROT MICHEL 11950 11977021993041369311286.53 8CANGL
+5913CITEAUX YVES 11950 61978031995082000715437.49 8MTECHN
+4766COLOMBE GUY 11950 81972041989101621213033.97 8CANGL
+8829CARCE ANNICK 21950 51970111991071578112723.22 8MSANS
+5482ENTREPIERRES DOMINIQUE 21950 81974081991081578112723.00 8MMARKT
+6118THEOPHILE PHILIPPE 11950 21970041995102666519900.36 8MSANS
+6158PUYGARD PHILIPPE 11950121971041993103916628549.82 8MSANS
+7664FRANCOIS HUBERT 11950 41974071993011369311287.46 8MANGL
+3674HOP VINATIER ANNE ODETTE 2195010199507199407 8359 7952.07 8MINFOR
+8401STADE YVES 11951121972051990011621213032.80 8MINFOR
+4992SOLEIL MURIEL NICOLE 21951101972021991072137316330.58 8MANGL
+4163FRESNAIE PHILIPPE 11951121977051991011684813423.49 8MALLEM
+6140VERTE ICHIKO 21951 51972071993042017815516.20 8MANGL
+7521SILLAT JOELLE 21951 71973081991021684813421.81 8MSANS
+8682HUYSMANS DANIEL 11951 61977051993011471512025.47 8CSANS
+4383JAVELOT CLAIRE 21952 21975041995101881314584.67 8MANGL
+3010CARLA ANNE 21952 21972081980102470018540.07 8MSANS
+5234BELLEGARDE GHISLAINE 21952 51972021991121608112916.45 8MSANS
+4958NAPOULE ANDREE 21952 71974011973011288210743.90 8MSANS
+8132ALDE PATRICK 11952 61971121989071621213032.29 8MSANS
+6296ALEXANDRIE CATHERINE 21952 11973051994041450311832.36 8MALLEM
+2851FEDERATION JACKY BRUNO 11952 419830419820411389 9851.21 8MSANS
+2922TRIERE JEAN PIERRE 11952 21972041989101621213032.87 8MESP
+4520BARONNETTES MICHEL 11952 51975121993011369311287.43 8MSANS
+7884LEPINE YAN MICH 11952 11986061995071288210745.04 8MSANS
+2916GRENADIERS LAURENT 11952 71977101991041578112721.89 8CSANS
+6995CIVRY MARILYN 21953111975041993041471512025.05 8MVENTE
+8042BERTHE DANIEL 11953 11976071993011369311286.57 8MSANS
+4389CARPATODE JACQUELINE 21953 31974051993012017815517.95 8MSANS
+4164THENIOUX THIERRY MICHEL 11953 31972071992072175716602.09 8DTECHN
+9986ARS DOMINIQUE 21953111974051991071578112721.65 8MMARKT
+2077VELETA CHRISTIAN 11953 81977121991071684813421.55 8MANGL
+7207VICT MICHEL 11954 71979091994041249910472.66 8CSANS
+0617ARBOUSIERS MARC 11954111981041991011608112915.62 8MANGL
+6266PALOMBES ALEXIS 11954121978061994011578112723.22 8MSANS
+3336REPUBLIQUE LOIC PIERRE 11954121986031991121288210745.67 8MTECHN
+6034LAMOTHE ROBERT 11955 71976081994011369311286.38 8MSANS
+8420COULEUVRE DIDIER BERNARD 11955 21978061994011369311286.30 8MVENTE
+7021INVILLE JEAN MICHEL 11955 11975051994012397418037.86 8MSANS
+2224EGALITE ISABELLE 21955121978031992021471512025.97 8MALLEM
+3309HELENE ISABELLE 21955 41986041992011288210745.22 8MSANS
+2341LANGUEDOC ANDRE 11955 61980081979081684813421.33 8MTECHN
+5599KOETZINGUE MARTINE 2195512199402199302 7803 7679.49 8MSANS
+1570ORB NATHALIE 21955 21976061992072043315669.60 8CTECHN
+0095DUNOISE DANIELLE 21955 21977041981011608112915.51 8MTECHN
+5288CHELLAH MARIE HELENE 21955 91976071991121578112721.88 8CALLEM
+9228CARDINALE PATRICK 11955 81979071994101249910472.03 8MSANS
+5668FOY MARC LAURENT 11955 419840319830710791 9503.63 8MSANS
+2007NEUVILLE PHILIPPE 11956 31976091991011578112721.79 8MSANS
+8560MAI DANIEL 11956121983041995101433211715.69 8MSANS
+6777PALAIS ANNE MARIE 21956 21977071976071834514274.96 8MSANS
+0514GRENIER MARIE CLAUDE 21956111978061992051471512025.43 8MANGL
+2450MEYRAN CHRISTOPHE 11956 91978081990052022115554.36 8MSANS
+0738CHATEAU SYLVIE 2195611199310199210 9894 8921.99 8MVENTE
+4115MIROIRS LINE 21957 71977071981091834514275.11 8MCOMPT
+2843BERRIERE THIERRY 11957 319930819940110876 9503.70 8MSANS
+1706KERDANIOU FREDERIQUE YVELINE 21957111979081993051608112916.01 8MTECHN
+3794MAL GERARD 11957 71989061993011326611016.03 8CANGL
+4102ZOUAVES ALAIN 11957 51980051986112256617145.74 8MMARKT
+6349GALLEU LAURENCE 21957101977021992011471512024.60 8MSANS
+1461BORGHESE TEVA 11957 91977081995041249910472.91 8MSANS
+1587CALIFO ROLAND 11958 6199305199201 9597 8729.39 8MSANS
+1182GRANCEY VALERIE 21958 11978031992021471512024.17 8CALLEM
+9993VALLON JEAN FRANCOIS 11958 21982031994011471512024.97 8MCOMPT
+3559OR CAROLINE 21959111979081993071471512025.77 8MSANS
+8859BERENGUIER MARTINE 21959 81980051993071527112373.49 8DSANS
+9596SINGER MARIE ODILE 21959111981081980081326611016.26 8MALLEM
+1614PIED JOCELYNE 21959101979081993071471512025.56 8MSANS
+7872CASTELET CATHERINE 21959 31980101979101684813422.12 8MSANS
+3066SCHWEITZER JEAN JACQUES MARIE 11959101984091993121326611016.80 8MSANS
+6764EGEOIRES DIDIER PIERRE 11959 519890819880810407 9271.56 8CANGL
+0114EGLISE ANDRE 11959121981101992101608112917.19 8MANGL
+8832ISOLA ERIC 11959121981061993121471512025.47 8MSANS
+0083BRUZETTES JEAN PAUL 11959 31984081991061326611016.78 8MSANS
+9690MAUPERTUIS GEORGES 11959 61981011989111608112915.03 8MANGL
+5294GIRONNETS MURIEL 21959 31981041980041684813421.54 8CSANS
+2982HORACE SATO 21959 7198609198507 9511 8689.07 8MSANS
+1765ARGE PHILIPPE 119591019930519920510024 8998.08 8CALLEM
+9648MOLENE HEYMAN 11960 21984111991092529918968.43 8MSANS
+2260BOULAZAC FRANCOISE 21960 11980101979101684813423.35 8MSANS
+0614RENOIR JEAN ADRIEN 11960 31983081993011215710279.72 8MSANS
+5022CARTOUCHERIE JACQUES 11960111984121994011578112723.60 8MSANS
+7019AULNES BETTY 21960121980091988051527112374.16 8MTECHN
+1225GRANIER DIDIER 11960121982091993011527112374.09 8MSANS
+0305CANTARON JACQUELINE ANNE 21960 21979111993101471512025.92 8MANGL
+9818ULBACH NATHALIE 21960 81982011981011326611018.09 8MALLEM
+6769TOURENNE MARIE HELENE 21960 81993081992011249910471.66 8MSANS
+4168CAILLE NELLY 21960 11980101979101684813423.35 8MSANS
+2371PILATTE PHILIPPE 11960121984071991061326611018.09 8MSANS
+1380SALLE BRUNO GEORGES 11960 31982011988112090215980.82 8MSANS
+1410REPUBLIQU ROZENN 21960 51983041982041608112915.17 8MSANS
+1668VALESCURE GILBERT 11961 619880319930111302 9775.91 8CESP
+4486CHANZY GILBERT 11961 81984061988052090215979.76 8MSANS
+4667CLAIR ANNICK 21961 51982071981071326611017.91 8MSANS
+3248CAMPANELLES MARTINE 21961 51982081989112090215981.29 8CSANS
+6422BARBELET THIERRY 11961 319810119800111131 9657.47 8MSANS
+5543JUMIN HENRY 1196111199405199201 9597 8729.85 8MANGL
+2561SAURSON MICHEL 11961 319820419900111131 9658.72 8MANGL
+3290CHARBONNEL PATRICIA 21961 7199607199507 8359 7952.19 8MSANS
+4075ORSAY JEAN MARC 11961 919890619950910407 9272.24 8MINFOR
+3928DAVID BERNARD 11961 51981091993121326611016.78 8MALLEM
+3502MONTGOINS ODILE 21961 91984091983091288210744.01 8CSANS
+2628MANET MICHELE 21961 51984091983091288210745.57 8MSANS
+3679LAY CAROLE 21961 81982101993071527112374.70 8CTECHN
+9476FERREOL PATRICE 11961 51985101993011168610047.54 8CALLEM
+6300VALLIER NICOLE 21961 3199207199107 8656 8146.11 8.ANGL
+2306RAHO BERNARD 11961 919900319890510407 9270.44 8MSANS
+7612VIEUX PASCAL CHRISTOPHE 11961101983051982051326611017.92 8MCOMPT
+9056CRAPONNE PHILIPPE 11961121985031989112090215981.60 8MSANS
+5891PRESIDENT ROBERT 11961 21992121993122401918074.91 8MSANS
+4133AIR PIERRE 11961 71981071991091326611016.35 8MSANS
+9028OUSTALET JEAN 11961 819821219811211131 9658.52 8MTECHN
+3462MONTEIL JACKY 11962 819820619810611131 9657.15 8MSANS
+7126JUSTICE MARIE PIERRE 2196211199504199404 7803 7680.66 8MSANS
+2705VILLA MIREILLE 21962 2199201199101 7803 7681.04 8.VENTE
+6788EXTE DOMINIQUE 21962101984111991091527112373.05 8MSANS
+6035AUMALE SYLVAIN 11962 619840319830311131 9658.70 8MSANS
+3359CERNOY PHILIPPE 11962121988061989091527112374.58 8CSANS
+8236CANAULET LOIC 11962 919870219870110407 9271.16 8MSANS
+2285FAURE NICOLE 21962101982071981071326611016.83 8MSANS
+6765PICARD AXEL 11963 61992121991121330611016.03 8CSANS
+6602COUDREE LAURENT MARCEL 11963111986051994111693613498.86 8MSANS
+6537BOTZARIS MANUEL 11963 91984031991091288210744.23 8MSANS
+9865CARDELINO ELISABETH 21963121986061985061288210744.19 8MSANS
+7936GAMBARDELLA MONIQUE 21963101984111983111288210743.92 8CSANS
+1490PAUTEL PATRICIA 21963 21993091992011202810241.21 8MSANS
+8828CAMP JEAN FRANCIS 11963 31983101993011949415050.78 8MSANS
+6652SURESNES BRIGITTE 21963121989081988081249910472.82 8DSANS
+9991PROF CHANTAL 21963 819950319940311389 9850.74 8MSANS
+4136ONDINE LAURENCE 21963 61984111983111608112915.66 8MTECHN
+2731DRIVE MICHEL 11963 81983051982051326611017.83 8MSANS
+7391SOIE BEATRICE MARIE 21964 11986101985101168610048.22 8CALLEM
+7157CEDEX GERARD 11964111991121990121330611016.53 8MSANS
+1010ARCES ELISABETH ANNE 2196412198811198606 8656 8145.62 8.ANGL
+7723GRATENTOUR FRANCOIS 11964121991051995081202810240.84 8MSANS
+7515JARUNSANITWONG PIERRE JEAN 11964 3199612199401 9597 8729.36 8CSANS
+7251ISIDRO DANIEL 11964 91988051987052090215980.28 8MSANS
+4749AVERTIN CLAUDE 11964101986071993041288210744.26 8MTECHN
+2395LOGNES HEDWIGE 21964 61984091983091288210744.23 8MSANS
+1171DELMAS DANIEL 11965 11988051992041249910473.45 8MTECHN
+8804MAHON FRANCINE 21965 51993061992061202810240.46 8MANGL
+0367DANTZIG JEAN 119651219940619930610363 9232.62 8CANGL
+4984SOULT ERIC ANDRE 11965 219931119921110024 8999.21 8CANGL
+5821POMPIDOU GINETTE 21965 51985061985061347711131.13 8MSANS
+6382LABOURD CATHERINE 21965 71989071995091249910472.66 8VTECHN
+0635WILLOWCREST JEAN CLAUDE 11965 71985071992121288210745.13 8MSANS
+0760PENIL PASCAL 119651019870119860110791 9501.81 8MTECHN
+6467HAVRE ANDREE 21965101987051986101527112374.07 8MSANS
+9897ONGUI PATRICE 11965 619940119930111389 9851.07 8CSANS
+5429HINODE FRANCOISE 21965 2199304199204 9126 8456.10 8MESP
+6900MANTEAUX GERARD 11966 719910219930610024 8997.99 8MSANS
+0370CASTELL MONIQUE 21966 41989031988031249910473.24 8MALLEM
+8517WASHINGTON BERNARD 119661219890119860410407 9272.17 8.ANGL
+9967TREMEUR HERVE MARIE 11966 81989081988081249910472.59 8SCOMPT
+6163MOLLARD JACQUES 11966 21993021992011249910473.00 8MANGL
+7120SOGNES HELENE 21966 31989031988031249910472.73 8MINFOR
+6709KERIMEL MICHEL 11966101988011987011539912453.47 8MANGL
+5303HETROLLIER MARIE LAURE 21966 119940919930411389 9852.11 8MANGL
+7075CEZAIRE JEAN LOUIS 11966 61985041992061527112374.63 8MTECHN
+5072SAUVETAT ALAIN 11966 919910519900510024 8999.27 8MANGL
+6154CALME MICHEL 11967 91988041991061249910472.82 8MANGL
+3329DOME PATRICE 119671219921119931110024 8999.31 8MSANS
+6319CALVAIRE YANICK 11967 119920819910810024 8998.01 8MSANS
+4968LYCEE MICHEL 11967 31991101990101467711987.34 8CSANS
+0009LOS JEROME 11967 2199611199511 8656 8146.86 8CSANS
+1878BEAUVILLE ERIK 11967 91990031994011249910473.09 8MSANS
+9800DORET CAROLINE LILIANE 21967 719930619920611389 9851.28 8MTECHN
+4797PORTOI MIVA 21967 61989111989111839014313.36 8MSANS
+7931LABERTRANNE SYLVETTE 21967 81994051992011202810241.97 8MTECHN
+8940BARRES MICHAELLE 21967 81990021993071330611016.66 8MSANS
+3164CROUY ANDRE 11967 71992091991091839014314.38 8MSANS
+1556CAMP VERONIQUE 21967 9199302199202 8656 8145.66 8.SANS
+2245FRINGANE VINCENT 11968 419940219920110024 8999.94 8CTECHN
+0888CURIE JEAN BERNARD 11968 91988091993121249910471.74 8MSANS
+3431BOUEL BERNARD 119681019880919870910407 9270.97 8MSANS
+3147ROCHERS ISABELLE 21968 51994071992011202810239.96 8CTECHN
+7236SIMENTAL BERNARD 11968 219910719910510407 9270.44 8MCOMPT
+9853YAN DAMIENNE 21968 71994011992011202810241.16 8CSANS
+3267LONG BRIGITTE 21968 11992091991091330611016.93 8MSANS
+8141EXUPERY GERALD 11968 51994011993011267210590.11 8CSANS
+3839MINIMES JEAN PIERRE 11968 71989011988011450311831.78 8MESP
+6386PAUCOURT SALOLOM 11969 91994011993011420611639.24 8MTECHN
+5948CARRERE JOCELYNE 21969 6199205199105 8656 8145.60 8.SANS
+1726ASQUES SYLVIE 21969101994021992011202810240.50 8MANGL
+3067CHEVRAINVILLIER BERTRAND 11969 119940119920110024 8999.90 8MSANS
+5762WINSTON JEAN 11969 11989101988101450311832.69 8MSANS
+6200MONTGOMERY DANIEL 11969 91990041995011330611016.47 8MSANS
+9177RIEUX BERNARD 119691119920619910611389 9852.27 8MINFOR
+6151CANTAGALLO EDDY FELIX 11969 71994011993011202810240.23 8MSANS
+8371MOUTHON HERMINE 21969 919950319930411389 9852.29 8MTECHN
+5982LONGWOOD MARIE NOELLE 21969 71991091991051330611016.84 8MVENTE
+3924ANGELES MARYSE 21970 219940719920111389 9850.74 8MSANS
+5000MARCHAND ANDRE 11970 8199306199206 9597 8727.91 8MSANS
+6053EXINCOURT DOMINIQUE 11970 419940619920110024 8999.43 8CESP
+6405ORMILLES ALAIN 119701119910819900810024 8999.57 8MSANS
+4367OURCHES AGNES 21970 1199506199406 7803 7680.36 8CSANS
+6055LACATE DENIS 11970 9199308199208 9597 8729.66 8CTECHN
+4701DUCA ANDRE 11970 71991051990051467711985.86 8CESP
+6470ALLEES YVON 11970 71991021990021467711987.75 8MALLEM
+6121RIVAGE FRANCOIS MARCEL 11970 419950119950110363 9233.88 8CSANS
+0867FOURS CHRISTINE 21970 3199409199309 8359 7952.10 8MTECHN
+9075SEMBAT FREDERIC 11970 3198912198812 8656 8145.26 8.ALLEM
+9637JUAN MURIEL 21970 5199310199210 8656 8146.70 8.TECHN
+6609GAYE NICOLE 21970 8199607199507 7803 7680.41 8MSANS
+5493LAUZES MICHEL 11970 11991071990071202810241.09 8MSANS
+6491BRAS BEATRICE 21971 619921019950410668 9425.45 8MSANS
+7556YOLA PHILIPPE 11971121994041993041360611209.94 8CTECHN
+7031ECOLE CHRISTINE EMILIE 21971 419910219920111389 9851.19 8MSANS
+5834ORANGERIE SERGE 11971 8199302199202 9597 8728.46 8CSANS
+4526LOT PHILIPPE 1197110199407199307 9597 8729.27 8CSANS
+4363QUART VINCENT 11971 4199404199201 9597 8728.44 8CSANS
+7702MONTARNAUD PIERRE 11971121992111991111420611638.97 8MVENTE
+8710GAULTIER ISABELLE 21971 31994011993011267210589.57 8CSANS
+5198ROUILLON CLAIRE LUCIE 21971 819940519920111389 9851.97 8CANGL
+6286WATTEAU JEAN CLAUDE 11971 81994071993071360611211.56 8MSANS
+3787TAUDE JACQUES 11971121993121994011202810241.40 8MSANS
+3968CAPRI MARIE CHRISTINE 21972 8199607199507 7803 7681.22 8CESP
+5435BERRE JOELLE 21972 21994091993091360611211.42 8CSANS
+2682VINGT GENEVIEVE 21972 7199406199306 7803 7681.40 8CSANS
+5935ORVEAU FRANCK 1197212199609199509 8656 8146.25 8MTECHN
+5312LUCE GUY 11972 81994041993041420611637.47 8MSANS
+5416ESSARTS FRANCOIS 11972 819931019930711389 9850.62 8CSANS
+5534ISLE MICHEL 1197210199312199212 9597 8728.58 8MSANS
+3974DEVIN FRANCOIS 1197212199605199505 8656 8145.57 8CSANS
+6877COUPERIN PHILIPPE 11972 5199310199210 9597 8727.95 8CSANS
+4740PLAISANCE SERGE 11972 219950119930410024 8998.82 8CSANS
+3393MANAU EDITH 21972 219941019930411389 9851.88 8MTECHN
+9615BOL JEAN CLAUDE 11972 21991071994011249910471.79 8MSANS
+4599GUYAU MAUD SOPHIE 21972 519940719920111389 9852.47 8MANGL
+8612HAMELIN JEAN MARC 11972 8199310199401 9597 8728.98 8CSANS
+8541RESIDENCE MICHEL 11972 319951219941210668 9426.89 8CTECHN
+3801NASTRINGUES SABINE 21973 8199606199506 7803 7679.46 8CTECHN
+2827OREE JOSEPH JEAN JACQUES 11973 6199502199402 9126 8456.78 8CANGL
+7660ESTIENNE JACQUES 11973 1199406199306 9126 8455.56 8MSANS
+5260JACQUES ERIC BERNARD 11973 319931119921111389 9851.61 8CSANS
+3671GARCHES GEORGES 11973 31992021991011202810240.68 8CANGL
+4348ROQUEFORT JEAN PIERRE 11973 819930319920311389 9850.74 8CINFOR
+6630LAGRANGE THIERRY 11973 9199302199505 9597 8729.63 8CTECHN
+9712EMPIRE CHRISTIANE 21973 6199606199506 7803 7680.67 8CSANS
+3751OASIS JACQUELINE 21973 219930319920711389 9851.25 8MSANS
+3830DRECHO JEAN 11973 61993051994011202810240.55 8CSANS
+4561DANNEMOIS JEROME 11973 5199612199601 7803 7679.91 8CSANS
+3191JAVEL CHRISTIAN 11973 5199311199211 8656 8147.13 8.SANS
+1618CHASLES CORINE 219731219940719930710668 9427.08 8MSANS
+4268SENE LUC 1197312199306199206 9597 8728.23 8CSANS
+8421ROCBARON JOEL 11974 1199510199410 7803 7680.36 8CSANS
+0930ESTANOVE ALAIN 1197411199606199506 7803 7679.99 8CMICRO
+6807TOURDRES MANUEL 11974121994051993051360611210.28 8CSANS
+4488CHAUVEAU CECILE 2197412199601199501 7803 7679.51 8CSANS
+8315COUVENT PIERRE 11974121995121994121450311831.06 8CSANS
+6605EDMOND SYLVIE 21974 8199504199404 7803 7679.78 8CTECHN
+9778ONS MARIE SIMONE 21974 7199405199406 7803 7679.96 8MSANS
+3816AURIC PASCAL 11974 6199502199402 9894 8922.63 8CSANS
+2018FOUR PATRICIA MICHELE 219741219940719930710668 9427.40 8CALLEM
+7643SEGUIER PATRICK 11974 5199406199306 7803 7679.67 8CSANS
+5610SAGET HENRY 11974 9199407199307 9126 8456.47 8CALLEM
+6425FINLAY PHILIPPE 11974 81993121994011202810241.74 8CANGL
+8616PAEA MICHEL 11975 5199407199406 7803 7681.40 8CSANS
+9958DULUD GILLES 11975 6199612199512 8656 8146.52 8.TECHN
+8573PIBONSON CHRISTOPHE FREDERIC 11975 6199607199507 7803 7680.81 8CSANS
+7803INCAPIS PASCALE NANCY 21975 7199604199504 7803 7680.34 8CALLEM
+7514VIENOT ANDRE 11975 3199508199408 7803 7679.78 8CSANS
+7507FAISANDERIE JEAN PIERRE 1197512199607199507 7803 7679.99 8CSANS
+3865MICHEL PATRICK 11976 3199507199407 7803 7680.12 8CSANS
+4755OCTAVE MARC 1197612199608199508 7803 7679.99 8CSANS
+0542TORREILLES ALAIN 11976 5199510199410 8656 8145.80 8CSANS
+9647DELORME DOMINIQUE PIERRETTE 2197612199407199307 7803 7679.69 8CSANS
+4725TACONNAZ BEATRICE 21976 4199508199408 7803 7681.17 8CALLEM
+9564CESSOLE FRANCIS 11976 4199607199507 7803 7681.20 8CSANS
+8087CHUTES GUY 11976 3199407199307 7803 7680.62 8CANGL
+5480LOUIS JACQUES 11977 9199607199507 7803 7680.59 8CSANS
+1361BANVILLE PATRICK 11977 4199607199507 7803 7680.34 8CSANS
+6209BEAURECUEI CATHERINE 21977 4199607199507 7803 7680.57 8CSANS
+3656FLIREY JEAN CLAUDE 1197711199606199506 7803 7679.59 8CANGL
+9051SUANE ALINE 21929 919680919870111389 9851.91 9VSANS
+2724DEL PIERRE 11930 21989021995023455825330.03 9.SANS
+7341GRATECA PIERRE 11933 21966071993012273717260.26 9MSANS
+6774BARRAU AGNES 21933 8199512199412 8656 8145.06 9MANGL
+0536FIRMAMENT SCHEHRAZAD 21934121962101982052470018541.72 9DSANS
+7368RESSAC RICHARD JOSEPH 11934 11967011994011557012567.09 9LANGL
+7599ANNE JEAN FRANCOIS 11935 11974031994011578112722.84 9VSANS
+1083COUCOURDE DOMINIQUE 11935101973061991011433211715.02 9MSANS
+2946ROLLAND OLIVIER 11936 51962111984122470018541.29 9MSANS
+7241THERMALE MARGUERITE 21936 7199306199206 9511 8688.67 9MESP
+1695LOUVEL CHRISTIAN 11937 519820219920311389 9852.69 9MSANS
+9744SALENGRO SYLVIAN ERIC 11937101961071991083417325097.15 9MESP
+7083OLIVETTE CATHERINE 21937101966071989031932314934.86 9MALLEM
+0151DUCHEMIN JEAN JACQUES 11938 41962061991012470018541.29 9MINFOR
+8395FRANCO ANNIE 21939101984011991091288210745.34 9MANGL
+9978BARBERAZ MARIE CLAUDE 21939 71972071989011621213034.09 9VANGL
+2131ARGENT CATHERINE 21939 71963121994042137316332.05 9MALLEM
+3235THUI HUGUES 11939 61967071995041557012566.37 9MSANS
+7994THIBAULT PATRICK 11939 31962051983032470018542.10 9MTECHN
+5783DOMATS MICHELE 21939121963031992021932314934.12 9MANGL
+6426PORTAIL JEAN FRANCOIS 11939 11969121995041557012567.53 9MESP
+6856JULIE JEAN MARIE 11940 319950219940211131 9657.83 9MANGL
+4024LEBRUN ALAIN 11940111965041995041557012567.77 9MESP
+9739NOCETA YVES 11940 21972041991011663613343.82 9MSANS
+9045MURET RENEE 21940101964091993072137316332.29 9CSANS
+4987UNIVERSITE CATHERINE 21940 41967011990011932314935.20 9MESP
+1530CYRILLE ANNICK 219401219741019881011131 9659.19 9VSANS
+1447HAMEAU OLIVIER 11940 61964081988092947921838.88 9MTECHN
+8268ANNEES PATRICK 11941 51961081992042022115555.75 9SANGL
+9303SCIOTOT ALICE 21941 61973071991101578112722.75 9MSANS
+0509PIECE ROBERT 11941 51963121994073844228045.38 9MSANS
+3288ISERE EMMANUEL 11941 51965081990011433211714.24 9MSANS
+9898CHANTEGRIVE JEAN MARC 11941 91964091992082470018540.12 9MSANS
+0163DAUTHEVILLE MARCELLO 11941111964121992072137316332.45 9MANGL
+0652VAUTHIER MARIE ALICE 21941 41971021991101932314933.60 9MINFOR
+7932VAUX DANIEL 11942 51969031991011433211713.80 9MSANS
+0197MAURENS CLAUDE 11942 91962081984123200223582.94 9MTECHN
+3251RUELLE JEAN 11942111976041992011403411520.12 9MALLEM
+8289OLYMPE NADINE 21942 51964121993102137316331.37 9MSANS
+9503GLORIETTE ALDO 119421019951219941210024 8999.12 9MSANS
+4563CERTAIN LOUIS 11942 31965071995011557012567.22 9MANGL
+6383DESGRANGES JEAN CLAUDE 11942 71983051993011249910473.49 9MSANS
+8658VALLONGUE BERNARD 11942 71963041995102666519899.30 9MESP
+4745RUCHERS GILLES 11942 21980051979051249910473.56 9MSANS
+8824PEYBERT JOCELYNE 21942 31963031994012137316332.51 9MSANS
+7242CORMEIL PATRICK 11942 81970041990011433211714.79 9MANGL
+0065ANGES XAVIER 11943101966101990011663613343.16 9VSANS
+4531SENART JEAN LOUIS 11943 21967111987012273717261.13 9MSANS
+7154MUIDS THIERRY 11943 31962121990013844228044.24 9MINFOR
+5345ORATOIRE ALAIN 11943121970081989011932314934.36 9MSANS
+3282SOUGERES JEAN LOUIS 11943 11968051990011433211715.33 9MSANS
+6215JOFFRE PASCALE 21943 81986021991071249910473.24 9MINFOR
+7867BASTIDE HIROKO 21943 51964071993072334017610.06 9MCOMPT
+6194STEPHAN KARIM 11943 81966051995041557012567.45 9MANGL
+4570MONDENARD JACQUES 11944 61965051994012137316331.93 9MINFOR
+3691CAORCHES MICHELE 21944101965081995042137316331.97 9MINFOR
+8595PRAD BENOIT PIERRE 11944111965031984123200223582.37 9MALLEM
+6617LONDRES PATRICK 11944 41972041991011621213032.39 9MSANS
+9701CAPUCINES ALAIN 11944 119820119830511389 9851.24 9MSANS
+7661ROSERAIE FRANCOIS 11944 31973071992011403411521.88 9MANGL
+0274FROMAGERIE ALAIN 11944121971011991101433211714.28 9CSANS
+9427GROULES MARIE FRANCOISE 21944 81963111993082334017609.06 9MANGL
+9749HEROLD ISABELLE 21944 31973071991071578112722.97 9DSANS
+5120MONTBY PIERRE 11945 81978041994011663613343.91 9MINFOR
+7337MAGINOT PHILIPPE 11945 61965031994012137316330.71 9MANGL
+2937PAYEN JEAN PAUL 11945 81973051992011403411520.53 9CANGL
+3034SCHUMANN GEORGES 11945 81970041991011433211715.11 9MSANS
+4582PETENATI MARIE CHRISTINE 21945 11974121993032043315669.65 9MALLEM
+2430TOURNELLES ALAIN 11945 91975031993011369311287.38 9MSANS
+4782CHENAY MARIE LAURENCE 21945121968091989012273717259.78 9MSANS
+1091RENNES MARIE ANGE 21945 61974071990071621213033.56 9MSANS
+2494ONA FRANCOISE 21946 51969081993121834514275.25 9MESP
+2170VIENNE MARIANNE 21946 81970021991102137316330.67 9VSANS
+2830FONTAINE ANNETTE 21946 41968041991091932314935.20 9MSANS
+8673ABEL JEAN PIERRE 11946 2198810198510 8656 8145.21 9.SANS
+9142ORSAY MICHEL 11946121970121991041663613343.28 9MSANS
+6458FOYER JEAN LUC JACKY 11946 71969111986012470018540.12 9MSANS
+8418RIVERIN ALAIN 11946 11979091992011249910473.05 9CCOMPT
+2785PONT ANDRE 11947 41968041990102739020403.33 9MANGL
+3079MOISE YVES 11947121972071991101433211714.69 9MTECHN
+6994CHEMINEES SOPHIE 21947 11976061981091834514276.09 9MANGL
+0913ROCHER BRIGITTE 21947101971041990102334017609.66 9MSANS
+3030LUZIADES ANTONINA 21947 519940619920111389 9851.64 9MSANS
+2592CLOUZIT NATHALIE 21947 7199208199108 8656 8145.20 9.SANS
+2816BAULE VERONIQUE 21948 51973031972031236910396.01 9MANGL
+0561DELAUNE JEAN NOEL 11948 11975041993011369311287.82 9MSANS
+4506DIDEROT JEAN PAUL 11948 619790519880411131 9657.51 9MTECHN
+5103CORNETIERE FRANCOISE 21948 21983061990041288210744.22 9DSANS
+0785RUDLOFF MICHEL 11949121971121993122547019086.15 9CSANS
+7465DEVORAH MICHEL 11949 91980041990091608112915.38 9MSANS
+6601LAS CLAUDE 11949 61969051990043417325096.67 9MTECHN
+5921SAINTPIERRE CAROLE 21950 31974021991071578112722.12 9CSANS
+4717BERARD DENIS 11950101983081987011283810705.67 9DTECHN
+6138DUCERIS COLETTE 21950 81992051991011202810241.85 9SVENTE
+1254ESQUIROL JEAN CHRISTIAN 11950 81975081992101369311286.84 9MANGL
+3226BOX CHRISTINE 21950 41970041990101834514275.65 9MSANS
+4683MAUNE PASCALE 21950 61974051995072175716601.42 9MSANS
+3496FROIDE JEAN JACQUES 11950111972101991012137316331.69 9MANGL
+2173BERNARD CHRISTIAN 11950 41972051991042137316331.34 9MANGL
+2204VIA STEPHAN 11951121972101991072017815517.05 9MSANS
+5528HORIZONA JEAN PAUL 11951 219931019940510407 9270.34 9MANGL
+2181CHAPUIS ALLAN 11951 1198810198603 8656 8145.89 9.SANS
+8003COMPREIGNAC JEAN RENE 11951 81973101994082201216756.14 9MALLEM
+4444HOTELS ANDRE 11951 41981111992011249910471.71 9CTECHN
+3137GARIDECH MICHELINE 21951121993051993041249910473.47 9MANGL
+5824LONGCHAMPS SYLVIE 21951 51972011990071578112722.42 9MANGL
+8477MENEZMEUR NORIKO 21951 51973071991041578112722.69 9MESP
+1336RANELAGH PIERRETTE 21952 21972041995011881314585.46 9MALLEM
+6782LAVACHET ALAIN 11952 11978091994011369311287.77 9MINFOR
+6451MONTPARNASSE AURIC 11952121973031987112470018540.56 9MESP
+9132BOUVELARD ALAIN 11952 619930819910110407 9271.74 9CSANS
+9726VIGIER GABRIEL 11952 419930819920811389 9852.47 9MSANS
+4608DESPAGNE ANNIE 21952 3199503199403 8656 8146.05 9MSANS
+2221LYANES YANN 11953121973071995041881314586.14 9MVENTE
+1575PELURES MICHEL 11953111975011991012781920674.59 9MSANS
+4898BLANCHET ODILE 21953101973071984051834514275.11 9MSANS
+2339ODES MICHEL 11953 31976121993041471512025.77 9MSANS
+4008BETHANCOURT MARIE ANNE 21953111981121980121202810241.36 9MSANS
+1590CIVRAC PHILIPPE 11953 91977051993041369311286.83 9MTECHN
+6109PEIRE GERARD ANDRE 11953 31972071991082175716602.51 9MCOMPT
+7232CHAMP MICHELINE 21953 7199001198801 8656 8145.87 9.SANS
+4789TOURACHE ALAIN 11954 71974081993011369311286.53 9MTECHN
+8709QUIETUDE MONIQUE 21954101977011994101471512025.14 9CSANS
+1712BIR ALAIN 11954 31975011991091684813421.42 9MSANS
+7069TUBY ROBERT 11954 71975061991071578112722.30 9MSANS
+4017BLEURY BRIGITTE 21954101977011992011471512025.20 9CSANS
+4255BEAUSEJOUR FRANCOISE 21955101981031995071433211714.70 9MSANS
+3435VENTS PHILIPPE 11955 71978041991011471512025.16 9MTECHN
+3130CIGALONS PHILIPPE PIERRE YVES11955121978111995021608112916.95 9MALLEM
+9170ALMA ANTOINE 11955111978101992011578112722.61 9MANGL
+9114CASTEL LILIANE 21955 51976071991121578112722.52 9MVENTE
+4844CHER JOELLE 21956 31993091995111202810240.94 9MINFOR
+1241GOUVERNEUR JEAN PAUL 11956 31976031990101684813422.68 9MSANS
+9766FRAYSSE CHRISTIAN 11956 819800719821211389 9850.88 9MSANS
+6781CHANTEREINE JEAN LUC 11956 91979011992012175716602.36 9MSANS
+6220PUYRAVEAU MICHEL 11956101985041988061326611017.52 9MSANS
+5187BOLIVAR DANIELE MARIE 21956 81981081995071433211714.30 9MSANS
+8283VICINAL MARCEL 11956 91977111992012175716600.94 9MSANS
+0990CORMERY EYT 11956111979121995041249910472.84 9MSANS
+9255SERRES MARY ANNE 21956 71981011980011684813421.58 9DANGL
+9968TRAILLES JEAN 11956 919870419860910407 9271.59 9MSANS
+6046RONDE JACQUES 11957101976121992011608112915.20 9MSANS
+4312DUROC KETTY 21957111980041979041684813421.51 9CANGL
+1998MADELEINE CATHERINE 21957 21980061994041471512025.70 9MSANS
+0810TROMPETTES LAURENCE GISELE 21957 91994021992011202810240.40 9SSANS
+1103JOURDANS JEAN MARIE 119571019900419910410791 9502.58 9MANGL
+3941SOUGRAIGNE MICHELINE 21957 71980071979071684813423.49 9CSANS
+1971BORDINAS GENEVIEVE 21957111980011993111471512024.89 9MSANS
+5459VORS ANNE 21957 5199006198906 8656 8146.61 9.SANS
+5423FLOQUET FABRICE 11957 3199408199308 8359 7953.59 9MSANS
+0905CARREL KARIN 21957101980101979101684813423.47 9MANGL
+4069JUZET JEAN FRANCOIS 11957101976071993062043315669.89 9MSANS
+4527MANIGUETS PIERRE 11958 819820619810611131 9658.16 9MSANS
+2372FONTANGE EMILIENNE 21958111981041980041381911366.00 9MANGL
+4357RAYOL PATRICIA 21958 41994031992011202810240.40 9MSANS
+5408BLANCHE GERARD 11958 41981021985112256617147.10 9MANGL
+3006FOULQUES JOELLE 21958 51979031993041471512026.00 9MINFOR
+6430PERIGORD PIERRE 11958 219790519780511389 9852.09 9CSANS
+6468PLANTADE BEATRICE 21958101981041980041684813422.18 9MALLEM
+0044MONTBRUN PIERRE 11959 51983061993121326611017.97 9MANGL
+2272GRANGE JEAN MICHEL 11959 519790319820111389 9851.46 9MSANS
+8185TONNER CHANTAL MARIE 21959 3199606199506 7803 7681.35 9MSANS
+8317DAMESME JOELLE 21959 919940219930211389 9851.73 9MSANS
+3554FRONTONAS MICHELINE 21959 61979101978101202810241.66 9MESP
+3926TARNES NELL 11959 11980101993041471512024.20 9MVENTE
+9173BLIGNY MARIE MADELEINE 21959 1198810198510 8656 8146.64 9.TECHN
+9704DEMMLER ANDREE 21959101979101993071471512024.66 9MANGL
+1648RENAULT MARIE ODILE 21959111986121991071249910472.96 9MSANS
+1803HONORE ISABELLE 21959111982041981041202810240.26 9MALLEM
+5795SUQUETTE SYLVIE 21959121980031995021527112374.40 9MANGL
+9158ALDERBURCHS PATRICE 11959 719790619811011389 9852.67 9MSANS
+8333PONCHETTES VIRGINIE 21959 7198801198510 8656 8145.83 9.SANS
+5238BOURRE CLAUDE 11960 51981031984091684813423.35 9CSANS
+7478ERDRE ANNE 21960 81993111992011202810240.71 9MANGL
+2036SALENGRO SYLVIANE 21960 31985121984121288210744.62 9CINFOR
+7716GOLF BERTRAND 11960121982071989021608112916.95 9MANGL
+4057MILTAT HERVE 11960 719810419800411131 9657.84 9MANGL
+2635BIGNAN MARTINE 21960 51981041992092090215980.88 9MSANS
+7613PIERRE JACQUES 11961121986041990031527112374.84 9MSANS
+0365CLICHY BERNARD 11961 419830519830511131 9658.07 9MSANS
+2806GOIZ DIDIER 11961 119840419830411131 9658.46 9MSANS
+4103BRUSC CATHERINE 21961 319940919930411389 9852.47 9MSANS
+1349MAUROIR ERIC 11961 819900119870310407 9270.29 9.SANS
+0451RENARD JEAN MICHEL 11961 1198810198604 8656 8145.78 9.SANS
+8494CYGNE SYLVIANE 21961111983021982021326611016.71 9CSANS
+9150GAY JEAN CLAUDE 119611119880719870810407 9271.52 9MSANS
+3221SURCOUF GUY 11961 91984081992121527112374.97 9MSANS
+3083CANADA ALAIN 11961 11982051982111326611017.29 9CSANS
+0448LAVANDES GILLES 11962 319930819920811389 9850.79 9MALLEM
+3439VERSAILLES MARC 11962 319840419830411131 9657.57 9MALLEM
+5360VEZELAY THIERRY 11962 119880919870910791 9502.46 9MSANS
+8567PRE FRANCOIS 11962 519910119940810407 9271.42 9MINFOR
+0702LACOSTE PHILIPPE 11962 61985091984091608112916.14 9MESP
+3016CARDELINE ELISE 21962 11993091992011202810241.27 9MSANS
+7387BELLERIVE EVELINE 21962 91992111992061330611016.03 9MSANS
+3882RETHACKER JEAN LOUIS 11962 719930819921210407 9272.17 9MSANS
+5174SEILLANS NICOLE 21962 71983081982081608112915.08 9MTECHN
+2800VILLETTE JEAN PIERRE 11962 51985041991091288210744.28 9MANGL
+9420ROURES YANNICK 119621019820319810311131 9658.82 9MSANS
+4001ASNIERES JEAN PIERRE 11962121987071994091693613499.67 9MSANS
+8856CANALS GILLES 11962 219911219901210024 8999.16 9MANGL
+9775MOUETTES FRANCOIS 119621219900219890210407 9270.91 9CVENTE
+7097BROUENOU JEAN PIERRE 11962 619860319850310791 9502.82 9MSANS
+8917CHANCELIER PATRICK 11962 31995021994021202810240.53 9MSANS
+8358CREBILLON NICOLE 21962 61983011982011608112916.37 9VSANS
+1296BERARD MICHELE 21962 119941019931011389 9850.89 9MSANS
+3188CONCY MICHELE 21962 31987091986092090215979.80 9MALLEM
+6829SEINE DANIELE 21962111994061993061202810240.23 9MALLEM
+7876VIELLA HERVE 11963 619930119920110407 9272.09 9CESP
+7396SANAJO BRUNO 11963 91987081991091249910473.60 9LSANS
+7380MARINA ANNE JACQUELINE 21963 119910919900910024 8999.61 9CANGL
+5941SIFFLETERIE GILLES 11963 519940219910710407 9270.56 9MTECHN
+5364BOUSSY GENEVIEVE 21963 519940519920111389 9850.61 9MANGL
+9502MONTOLIEU JEAN CLAUDE 11963 41985051994061527112374.72 9MSANS
+8964DOUJAT MICHEL 11963 619841119831110791 9503.92 9MSANS
+3405AMANDIERS PHILIPPE 11963 81984031992111527112372.90 9MSANS
+1868COUPE JEAN 11963 61993031995081381911366.16 9MSANS
+2984GOUSTAN JEAN MARC 11963101987111994091693613499.09 9CSANS
+7572CHARONNE DANIELLE 21964 41995011994011206910240.19 9MALLEM
+9668EDELWEISS JEAN BERNARD 11964 31984071991121288210745.70 9MSANS
+3335CANTEPERDRIX MARIE CHRISTINE 21964 41983061982061326611017.52 9MSANS
+2509FINO JEAN MICHEL 1196410199311199211 9085 8417.90 9CVENTE
+1812LLORCA PIERRE 11964 21986031993091527112374.72 9MTECHN
+5943BERGERIE JEAN 11964 31987021995011373711327.16 9MSANS
+5136VALERIEN ANDRE PASCAL 11964 71987081986081527112373.86 9MANGL
+6432ECUREUILS PATRICK 11964 3199202199102 8656 8147.19 9.SANS
+2610DUVERGIER MARC 11964 91984031992031527112374.88 9MTECHN
+5455CEZANNE PASCAL LUC 11965 31988091993031450311831.19 9CALLEM
+7204PICCINI CAROLINE 21965121992091991091839014314.08 9CSANS
+0125HUNTINGTON YVONNE 21965 91994011992011202810240.77 9MSANS
+3791BOUISSETTE PHILIPPE 119651119940119950810024 8998.95 9CSANS
+2711ROSETTE PHILIPPE 11965 41990101994011288210744.86 9CALLEM
+4882LOZERE ERIC 11965 31988021992061450311831.91 9MANGL
+4855BELLEVUE PATRICK 11965 11990101989101249910473.69 9MALLEM
+3048PIERRE PIERRE 11965 7199110199010 8656 8146.61 9.ANGL
+2278LOGT NORBERT 119651019900619890610407 9270.24 9MSANS
+5557LAUZADEL GERARD 11965 91992071994072256617145.75 9MSANS
+6129ROZ GISELE 2196510198805198510 8656 8145.20 9.ALLEM
+3979RIVES YVES 1196511199609199509 9126 8455.98 9MTECHN
+8688FASSUN HUGUETTE 21965 4199511199411 7803 7680.23 9MSANS
+9402PARMENTIER PATRICK 11966 819880319870310407 9270.16 9MCOMPT
+8428BARJAQUETS THIERRY ABEL 119661119920319910310024 8999.07 9CALLEM
+8348CLERMONT ANNE MARIE 21966 11989051988051202810241.40 9MVENTE
+3723COURCELLE GENEVIEVE 21966 2199506199406 7803 7680.93 9MANGL
+3173CHAL ARMAND 11966101986051988061288210745.88 9MSANS
+9965VENETES YOLANDE 21966 519930719920711389 9851.21 9MTECHN
+6306ANJOU ALINE SIMONE 2196612199004198707 8656 8147.13 9.ANGL
+5448FILHOS JEAN CHARLES HERVE 1196612199212199112 7677 7604.70 9MSANS
+7571GORRE SYLVIE 21966101988121987121249910472.03 9MSANS
+8890LAMORLAYE FREDERIC 11966 41986041992011450311832.68 9MVENTE
+2287DORMOY GENEVIEVE 21966 31988111987111450311830.80 9MSANS
+0092ALCANAL JEAN LUC 11966 61987041992121249910472.03 9CSANS
+2554ROULOTTE OLIVIER 11966 419940219920110024 8999.03 9MANGL
+9801GRAPPONS NATHALIE 21966 919940819930411389 9851.57 9MTECHN
+1541ALDES JEAN MICHEL 11967121994051992011202810240.26 9MSANS
+9290PLOEMEL YVES 11967121991051990051467711986.91 9MSANS
+8169CHAUVIN FRANCOIS 11967 21994011994011202810240.53 9MSANS
+6269GOLF ROLAND 11967 919940219940710024 8999.47 9MALLEM
+2878RICHART ERIC JEAN MARIE 11967 819891219950410407 9271.91 9MSANS
+9098OR DANIEL 11967 719880919870910407 9272.01 9CSANS
+4331MONIER DIDIER 11967 719881019871010407 9270.12 9CSANS
+0261ROUQUET RENE 11967 41989051988051539912452.78 9MSANS
+1507FLACHAT PIERRE 11967 41994011993011420611639.09 9CSANS
+7887TENDRE CLAUDE ANNE 21967121994051992011202810241.72 9DSANS
+0525SERRES LAURENCE MAGALI 21967 11994011992011202810240.50 9MSANS
+9421ASNIERES MARIE CHRISTINE 21967 319951119930411389 9852.22 9MSANS
+3624LAPEYRE THIERRY 11967 519870119940110407 9270.12 9MSANS
+0344GABRIEL JEAN PIERRE 11967 419940119920110024 8999.57 9MINFOR
+7208GAZAN ERIC JEAN 11967 219940119950810024 8998.94 9MTECHN
+4335GLACIERE KEIKO 21967 21994041992011202810240.89 9MINFOR
+9851TOUCANS MICHEL 11968 219910119900110407 9272.01 9MTECHN
+9263ROSSINI ALAIN 11968111991011995021330611018.13 9MSANS
+9662DEPORT PHILIPPE GEORGES 11968 3199607199507 8656 8145.38 9MANGL
+2814ALFRED VERONIQUE 21968 6199202199102 8656 8146.65 9.COMPT
+3291LACUEE REMI 11968 91989021988021450311831.34 9MANGL
+3557KERHOUET REGIS 119681019930319920310024 8997.86 9MSANS
+6204ESCA BRIGITTE 21968 9199404199201 9597 8729.22 9CESP
+1678HADRIEN PHILIPPE 11968 819931219921210363 9231.95 9CSANS
+0299MONICA DOMINIQUE 21968 51994021992011202810241.70 9MTECHN
+6960PRES COLETTE 21968 2199108199008 8656 8147.15 9.SANS
+4443BENOIT JOSIANE 21968 81988011992011249910473.05 9MTECHN
+5697QUILICHINI ROBERT 11968 31993121992011202810241.92 9MINFOR
+9315PAVE LEON 11968 21988111987111539912452.39 9CTECHN
+7230BOUCHE FRANCOISE 21969 3199304199204 9126 8457.67 9MSANS
+8237DENEB WALTER 11969 419890919910710407 9271.47 9CSANS
+3459ALISCAMPS LEONIE 2196912199610199510 8656 8146.02 9MANGL
+4247ARISTIDE CHRISTIAN 11969 5199501199401 9894 8921.55 9MSANS
+0137VILLEMENT ANNE MARIE 21969 81994051992011202810239.81 9MSANS
+9445STREET DANIELLE 21969 7199204199104 8656 8146.74 9MSANS
+9019VALLIERES JEAN 11969121990111989111381911366.90 9MALLEM
+4031COLVERTS LAURENCE 21969 11993121992011202810240.89 9MSANS
+4000CUENANT THEOPHILE 11969 219890619880610024 8998.09 9CSANS
+7496GRASS FRANCOIS 11969 81989051992121249910472.43 9CSANS
+4671GRASSET ROGER 11969111992061995121330611018.09 9CSANS
+0067RESTAURANT MICHEL 11970 7199407199307 9597 8728.26 9MANGL
+0824FIGARES JACQUES 11970121993101992101360611210.03 9CESP
+0143MACONNERIE MICHEL 1197010199511199411 7803 7679.87 9MSANS
+2934FUZELIER SYLVIANE 21970 5198906198806 8656 8146.56 9MSANS
+2663TOULOUSE PATRICK 119701219930819920811389 9850.66 9CSANS
+0038LANGRUNE FRANCOIS 119711119940619951010024 8998.32 9CANGL
+8414VILLE NOEL 11971 41990051989051202810241.49 9CANGL
+5333SQUARE JEAN LUC 11971 81995051994071450311832.45 9CSANS
+3680LAUTREC CALIXTE 21971 9199602199502 7803 7680.84 9MINFOR
+9370CAMELIAS CHRISTIAN 11971 5199307199207 9597 8729.90 9MTECHN
+6026NADAILLAC JEAN LOUIS 119711019910219900210024 8998.44 9CANGL
+4310VICTORIA ERIC 1197110199607199507 7803 7680.44 9CSANS
+2394CLAVIERS MICHELE RENEE 219711019910919900910024 8998.26 9CANGL
+4996GUILLAUME FREDERIC HENRI 11971 41993031992031330611016.30 9CSANS
+6930ROYAL EVELYNE 219711219940119930110668 9426.26 9MSANS
+1006CRAU MARIE CLAIRE 21971 21994051992011202810241.04 9MSANS
+0817ATHEE MAITE 21971 81993011992011267210590.11 9MSANS
+5245VAL XAVIER 11971 3199306199206 8656 8146.79 9MESP
+7249CLARS ALFRED 1197110199306199206 7803 7679.41 9.ALLEM
+9895JUVANTE JACQUES 11971 61995051994051450311832.69 9MSANS
+4769BORDERIE OLIVIER JEAN 11972 219940719930711389 9852.20 9MTECHN
+4620VERDEILLE FRANCIS 11972 619930419940910363 9232.32 9CSANS
+3812BLAINS DIDIER 11972 9199302199202 9597 8729.94 9MANGL
+9910FORET JEAN CLAUDE 11972 119930719921011389 9851.30 9CSANS
+5930CHATEAU SERGE 11972 9199205199105 9597 8728.49 9CMICRO
+3132SAGONE EMMANUEL 21972 4199601199501 7803 7679.46 9MSANS
+2171BERGERONNETTES STEPHANE 11972121995111994111450311832.22 9MSANS
+5929CARRIEREBLANCHE OLIVIER 11973 6199410199310 9597 8728.98 9CINFOR
+6111ROCHETTE JACQUES 1197310199307199207 7803 7680.93 9.ANGL
+1480CREYSSAC VANESSA 21973 4199511199411 7803 7679.33 9CSANS
+9433BIS NICOLE 21973 6199407199307 8359 7951.65 9MALLEM
+5673ROLLIN JEAN 11973 6199304199204 9597 8728.28 9CESP
+8786MIMOSAS ALAIN 11973 21993031993061420611637.51 9CANGL
+3099MAUR ANDRE 11973 41996051995051450311830.65 9CANGL
+1278ENTASSI VERONIQUE 21973 7199606199506 7803 7679.67 9CSANS
+6956PRIEURE FRANCOISE 21973 819930919920911389 9851.28 9CSANS
+7084AVNEUE JACQUELINE 21974 7199606199506 7803 7679.99 9MANGL
+9484DOUMER JOEL 11974 519930819940910363 9232.98 9CESP
+9483VILLEBON REMY 11974 8199606199506 7803 7679.78 9CSANS
+4120SURBAIX FRANCOIS 11974 719940819930810668 9425.81 9CSANS
+6136PASSERO JEAN PAUL 11974 4199606199506 7803 7679.78 9CANGL
+9119SERVAN VERONIQUE 21975 7199505199405 7803 7680.00 9CANGL
+0958RENDEZVOUS BARBARA 21975 6199307199207 7803 7679.87 9.TECHN
+0221ANDALUCIA SERGE 11975 1199606199506 4264 5818.98 9CANGL
+1770MARIGOT DIDIER 11975 9199407199307 7803 7679.94 9CCOMPT
+2889MANE JACQUES 11975 5199606199506 7803 7680.21 9CINFOR
+8800CHEZ JEAN 11975 5199408199308 7803 7680.98 9CANGL
+6217MURE GREGOIRE ROBERT 11976 5199607199507 7803 7679.28 9CANGL
+9488HARMAS JOSE 11976 8199607199507 7803 7680.27 9CANGL
+9074MILIERE ELIANE 21976 6199607199507 7803 7681.26 9CSANS
+8755BOITRON MICHEL 11976 4199606199506 7803 7679.67 9CSANS
+5066LOGES PATRICK 11976 2199401199301 7803 7680.72 9CSANS
+7767CAVOK CATHERINE 21976 4199506199406 7803 7679.49 9CANGL
+4014DELAMBRE ALAIN 11976 4199507199407 7803 7680.71 9CSANS
+7434CHAMPCEVINEL MICHELINE 2197612199606199506 7803 7681.25 9CSANS
+3520CATALINA CATHERINE MARIE HELE21976 9199506199406 7803 7681.34 9CVENTE
+6859BILLET JEAN LOUIS 11977 7199606199506 7803 7680.36 9CALLEM
+3181NADAUDS JEAN MARC 11977 2199506199507 7803 7679.51 9CANGL
+0928DELACROIX CATHERINE 21977 6199607199507 7803 7681.13 9CALLEM
+5100BREVAINVILLE GERALD 11977 8199607199507 7803 7679.45 9CSANS
+2336ROCAMADOUR GILLES RENE 119331219861119901011131 9658.9910MINFOR
+3692ALBIZZIAS DOMINIQUE 11935121965031994011557012566.3610MSANS
+1060SEDS ANNE MARIE 21935 61970071991071578112722.8210VANGL
+1596OPERA JEAN LUC 11935 31971101991041433211714.3910MSANS
+2633SOULEYRAS MONIQUE 21935111958071993012470018540.4410MINFOR
+0658LOURMEL BERNARD 11936 51967051994011932314934.8010MTECHN
+1362LAC YUMIKO 21937 41965061994012137316332.6910MSANS
+7201ILE JEAN PASCAL 1193711198810198309 8656 8145.1210.SANS
+7674CASTE PIERRE 11937121971061993072547019086.2710MVENTE
+9244LIVRY EDITH 21937 1198901198306 8656 8145.3810.INFOR
+4790FRANCIS LYSIANE 21937 2199001198903 8656 8146.5210.SANS
+7950CHARBONNIERE STEPHAN 11938 61972051991011433211714.5510MSANS
+1814IRMA MARIE 21938101967011989091932314935.3810CANGL
+0599FAUBOURG BERNARD 11938 71961011986012273717261.9010MSANS
+0425PLUVIERS JEAN PIERRE 11939121971071995081557012567.3610MSANS
+7312EST THIERRY 11939111971071992011433211715.4510MANGL
+2010CAMP LAURENT 11939 319890119931211389 9852.1510CTECHN
+3770COSTES ELIANE 21939 91980071992121471512024.9610CSANS
+3046CRIMEE JACQUES 11939 719890619880610494 9309.0810MANGL
+5231BONNETERIE VERONIQUE 21940 61962021988042273717260.8010VINFOR
+1917CENTRE FREDERICK HENRI 11940 31961091990012739020404.0510MALLEM
+0890MIEGESOLLES BERNARD 11940 21960041995043200223583.4110MESP
+5720SAULX JACQUES 11941 71971071990071663613343.3310MSANS
+4881DESFOSSEZ JULIETTE 21941121976051991051684813422.8110MSANS
+5085ALBERIC PHILIPPE 11941 919900719950310407 9271.7010MANGL
+0632BESSE PASCAL 11941 51966021989071663613343.7310MSANS
+5114SHARON MICHEL 11941 21968081984062022115555.3510MSANS
+4229TERRASSE BERNARD 11942 11964111993102137316332.4710MSANS
+2811BROSSOLETTE GEORGES 11942 11967121990011663613343.0610MSANS
+0937TILLEULS BRUNO 11942 81972061992011433211714.9110MANGL
+6193ROOSEVELT CHRISTIAN 11942 71972061991011663613344.1510DTECHN
+2942PETEL GEORGES 11942 21974051992071369311287.8610MTECHN
+9989TASSIGNY JEAN PATRICK 11942 91971071992011403411520.6610MANGL
+1548BARBIER RENAUD JEAN 11942 81961081986042273717260.1910VINFOR
+9612LAURAGAIS PHILIP 11943111972031995042547019084.9710MCOMPT
+0194FOUQUEVILLE BRIGITTE 21943 51963041991072273717261.1310MSANS
+2327GAVOTTE JEAN MARC 11943 11974051993011369311287.1010MSANS
+9716HENRI JACQUES 11943 31963041993032922321685.2910MANGL
+2720LOCMIQUEL BERNARD 11943 6199301199201 8656 8145.4410.SANS
+1736OLIVERAIE VERONIQUE MICHELLE 21943111971071991071578112723.5010DANGL
+5205PIOCH VERONIQUE 2194412199302199202 8656 8145.2610.SANS
+3085TRASTOUR GERARD 11944 61972041990011433211714.0710DSANS
+0317MOUETTE THERESE 21944 51972051988101621213032.5110MSANS
+5694PIERRELAIS ERIC 11945 81972091976082022115555.4410CANGL
+1116BOISSISE ANNE MARIE 21945 1198901198606 8359 7952.1210.ANGL
+4722BRIMONT MARTINE 21945 5199007198907 8656 8147.1710.INFOR
+3958CRAMCHABAN MICHEL 11945121969011990101433211714.5110MSANS
+7673SURESNES JEAN PIERRE 11945 61982011992101249910472.1010DSANS
+7687VALET JACQUES 11945 61972071989011621213032.1710MANGL
+8152TAILLIS FRANCOIS 21946 91976021991101578112722.6610MCOMPT
+0924BONAVENTURE REIKO 21946 91971011993042547019086.4510MVENTE
+8336MASSUGAS MICHELLE 21946111968111992091834514274.3910MSANS
+8834LEMENC DIDIER 11946 11974031992102334017609.9510MSANS
+6052DURAND LAURENT 11946 21972121988061932314934.9910MSANS
+8405GAULLE JACQUES FRANCOIS 11946 31969051989012273717261.7510MSANS
+7901VIOLAINE JACQUES 11946 419840719900711131 9659.1710MALLEM
+9538SUEDE JEAN MICHEL 11946101971021991011433211715.6910MSANS
+7317MARMONT ALAIN 11947 91973051991071621213032.1210MSANS
+8240ASSOMPTION JACQUES 11947 11973011991072837121100.6710MALLEM
+8104FRERE DOMINIQUE 11947 11975041993011369311286.0310MVENTE
+6906VILLEFRANQUE FRANCOISE 2194810199107199007 8656 8145.4410.MARKT
+4316DOMDES NOELLE 21948 51971011989082022115554.9910MSANS
+8058RANGE MARIE CLAUDE 21949111973061991071578112722.2410MSANS
+6117WILSON MICHELINE 21949 21970011993111834514274.5310CSANS
+2977CERISAIE CAROLE PAULETTE 21949 81972051993111471512025.8210MCOMPT
+3294PLOUER MICHEL 11949101969101989042022115555.4110MCOMPT
+4908GIRONDE PATRICK 11949 41981091992011249910472.6910MSANS
+2440AJAC FRANCOIS 11949121974081990011684813423.2610MSANS
+6866MUSTAPHA VALERIE 21949 71970111994071834514275.3810MALLEM
+9238CHOISY JACQUES 11950101976081992042022115555.4810MSANS
+5421NATIONALE FRANCOISE JEANNE 21950 21971061995121684813422.3610MSANS
+7441ROCHE CLAUDE 11950121977051993041369311288.0110MINFOR
+2053LOIR MONIQUE 21951121972061990011621213032.0610MANGL
+6550BELLEVUE FREDERIC DAVID 11951121970111990122470018540.4210MANGL
+0900PAT VICENTE 11952 61977071991011369311287.7710MINFOR
+9719BERAL YVON 11952101986101994011433211714.3710MVENTE
+8718VIENNO DOMINIQUE 11952121976101991011369311287.8310MSANS
+5348CRIKET JEAN 11953121979051995041471512024.2110MSANS
+7442DETAILLE JOEL 11953 81973031991012334017608.5310MSANS
+7704MARTILLE CATHERINE 21953 21973041991072137316330.5310CANGL
+3122MADELEINE CLAUDE 11953 31978061995041471512024.7810MANGL
+7454AURAY MARC 11953121972121992122175716601.7510MSANS
+2338LAMARTINE DOMINIQUE 11953 81981051992011471512025.4710MSANS
+5898ITALIE DENIS 11955 419941119920110791 9502.4110MSANS
+2818ENTREPRENEURS BRIGITTE 2195610199506199406 8359 7951.8010MSANS
+4736MONTBRIEUX LAURENT 11956 11982051991071471512024.9210MSANS
+5607ORBAY GILBERT 11956121976071994011369311288.0110MTECHN
+6044BRONZE CHANTAL 21956 31978031990101911114779.0510CESP
+2949AIGREFEUILLE HERVE 119561119810119840811389 9851.9210MTECHN
+1891POSTALE DIDIER 119561019820319810611131 9658.0110MSANS
+6887ESTADOU MARC 1195712199311199211 8656 8146.8810.COMPT
+3156FLACQ PHILIPPE 11957 11980081992011608112915.1510MINFOR
+0665MIRAGE HERVE 11957 41978031995041249910471.8810MSANS
+9619SOLEIL SYLVIE 2195811199406199507 9126 8456.9610MTECHN
+7598COTES MARC 11958 41978041995041249910471.9410MINFOR
+1289SOUCI ANNE 21958101980071979071194310202.5910MSANS
+5278SEYSSUEL ROSELYNE 21958 51979021993071608112917.1010MSANS
+5065INDUSTRIE JEAN PIERRE 11958 81978071995041249910473.1510CSANS
+6257PLESSIS JEAN YVES 11959 51983041994011433211714.2810MVENTE
+5018CHAUX JEAN 11959 91979061993041471512026.1810MTECHN
+4009PLEIN ANTHONY 11959 719810119800511389 9850.5610MTECHN
+9730PRINCE JEAN JACQUES 11960 31982051991091326611017.5010CSANS
+1357TAMNIES FRANCOISE 21960 11980031979031215710279.3810MSANS
+8601VALMY PIERRE M 119601019820319810311131 9657.9610MTECHN
+5371ARC GILBERT 11960111988101996012773220636.1610.INFOR
+0267OLIVIERS DIDIER 11960 7199312199212 9597 8728.6410MSANS
+3866FLEURIS JEAN MICHEL 11960 41986021993041288210744.6410MTECHN
+4485ALLEMANE PHILIPPE 11960 71983011989041608112915.2410MSANS
+1801CAUTEGRIL CORINNE 21961 11981031992061527112372.9010CTECHN
+0557BENEFIAT MARTINE 2196111199412199312 7803 7680.7710MSANS
+2605MATHURIN PIERRE 119611019830419820411131 9658.4710MINFOR
+8035BASSETTE BERTRAND 11961 31987081986081288210743.8610MANGL
+4026GAJAC KARIN 21962 81983011982011608112916.3210MSANS
+8790TREDREZ LEONIE 21962 71986061985061288210745.2210MSANS
+0932BASTIDON MARYLENE MARTHE 21962 7199301199201 8656 8146.4210.SANS
+1052MARJOLAINE PHILIPPE 11962 11984111991071868714507.4710MSANS
+3917FONSORBES JOEL YVES 11962111982061988051326611016.6210MALLEM
+6770ROUBIN FREDERIC MARC 11962 719840419870211131 9658.4110CSANS
+9591BAS EDOUARD 11963 319850419841210791 9503.5810MSANS
+0126LALA MICHEL 11963 219830619820611131 9658.7410CESP
+0547GAUMERIE EDGAR 11963121985011994011433211714.2510MANGL
+5064LOISIRS GEORGES 119631019851219860510791 9503.6610MALLEM
+6975BOUCHET YANNICK 11964 61985111985112090215981.0610MESP
+2365TERNES ERIC 11964 51983081994011433211714.4310VVENTE
+1287OIDE MARIE LOUISE 2196412199201198703 8656 8146.9710.SANS
+1746MICHELETTE GERARD 11964 619840719830710791 9503.7410MSANS
+4496COLONEL MONIQUE 21964 41984091983091288210744.8610CSANS
+6420MERCURE MICHEL 11965 61985121993121288210745.0010MSANS
+2452FEUDON ANNICK 21965 81989061988061249910471.8910MANGL
+3490DION ALEXANDRA YVETTE 21965 6199504199404 7803 7680.5910MVENTE
+9441GALLET JEAN CLAUDE 11965 11991021994022256617147.1310MSANS
+8588JARRES GERARD 11966 919880319870310407 9271.5610MSANS
+1291HERMITAGE XAVIER 11966111991101990101330611016.3310CSANS
+6530ETRUN PATRICK 11966 31989111988111539912452.4210MANGL
+1352PLAIS JUAN CARLOS 11967 419870719860710407 9270.9110MSANS
+7277REY MICHEL 11967 21995081996012559919162.3610CSANS
+1793BARBE VALERIE ANNE 21967 5199201199101 7803 7680.5910.INFOR
+3158KERIOLET FRANCOIS CHRISTOPHE 11967 41990051992121381911365.8510CSANS
+0027DANS JEAN PIERRE 11967 719920119910110024 8999.9910MSANS
+6374PUISEAU THUY LAN 11967 61987041991121249910472.2410MESP
+9025BEUVRON PATRICK 1196711199606199506 7803 7679.4610CANGL
+9820CHARLES CATHERINE 2196710199507199407 8656 8146.4610MALLEM
+6484VIGNAUD PATRICK 11969 91990071992031381911365.6710MSANS
+1414BRUNE SYLVIE 21969 619890219880211302 9775.1910MSANS
+4633MICHELET JOHAN FR 11970 619901219891210024 8999.7210MINFOR
+2182PLANTIER PHILIPPE 11971101995091994091450311831.5510CSANS
+8157LILAS PIERRE 1197210199207199107 8656 8146.5510.SANS
+5454LOIR MARIA 21973 5199406199306 7803 7680.1410CANGL
+9209CARREFOUR SERGE 11974 6199505199404 9126 8455.7410MALLEM
+3395ADAM JEAN CLAUDE 11974111993091994011202810241.5810CSANS
+8662REIGNIER JEAN MICHEL 11975 9199607199507 7803 7681.4710CSANS
+8679MOUTONNE MARIANNE 21935 9199408199201 9085 8419.1611DCOMPT
+9359RASPAIL ALAIN 11935 91963121994103200223582.5411MTECHN
+0977DELBESSOU JEAN CLAUDE 11935 91958051992013844228044.4211MANGL
+5628REPUBLIQUE PHILIPPE 11935111967011995041557012567.7211MALLEM
+5361CHAUMIERE CHANTAL 21937 91967011992011471512024.5111MANGL
+8814KIKOCHI CHRISTINE 21939 819880519870510407 9271.4611MTECHN
+3550CONCLUE PAULETTE 21941 21973081991071578112723.7211MTECHN
+3525PAU CLAIRE 21942 81965021991122334017609.2411DSANS
+6963CEZANNE CAROLE 2194310199411199311 8656 8146.7911MALLEM
+1837FRIZAC PETER 11943 81976041995011578112721.6711MINFOR
+5658SOLANGIERE JEAN 11944121969121990041433211715.0511MSANS
+7261MONTGERMONT SOPHIE JEANNINE 21945111971111995041834514274.5111MANGL
+4785LODI PHILIPPE 11945 21980071994011471512024.5611MESP
+5841PORT JACQUELINE 21946 41970021969021300910822.2611MANGL
+0606BAGEN MARIE JEANNE 21946 11970121993032022115555.6811MSANS
+5640GALLIA BENEDICTE 21946101973101993121911114780.1311MINFOR
+0298BRETIGNY MICHELINE 21946 5199208199108 8656 8145.8011.TECHN
+3925PELLETIER MARTINE 21946111968111993012137316332.0911MSANS
+7692BARBIN MARIE NOELLE 21948 21968111990012137316331.0611CINFOR
+3739JUIN MARCEL 11948111981091992011249910472.7411MSANS
+7300URANIES CHRISTIAN 11948 91973021992011433211714.6011MTECHN
+6456TUBLERIE CAROLE 21949 81968111992032470018540.0611MANGL
+3324TAMANACO MARTIAL 11949 21974061993011369311288.2211MSANS
+8400LOIRE MURIEL 21949 41969031991091834514275.4511MSANS
+9140RENAITRIE CLAUDE 11949101980071995041249910473.7211MSANS
+5589SITE PATRICIA 21949 7199508199408 7803 7679.4611MVENTE
+5386MEDECIN JACKY 11950 21975051992101369311286.8711MSANS
+2377VOLTAIRE CHRISTIAN 11951111976101993041471512025.2511MSANS
+4340BAUDRIMONT JEAN FRANCOIS 11951 41984071993121326611017.5511MINFOR
+0397PAREAGE JEAN YVES 11951 81979071994101249910472.9611MANGL
+9259PEYRAS CHANTAL 21951 4199608199508 7803 7679.6411MVENTE
+1711ESTAGNOL BRUNO CLAUDE 11951121983051992071578112722.6611MINFOR
+4351CHARRON ANNICK DENISE 2195210199408199308 8359 7952.4311MALLEM
+2396CHAPELLE JOCELYNE 21952 41976061975061288210745.0911MANGL
+3571OBSERVATOIR NICOLE 2195211199610199510 7803 7679.9911MSANS
+2219LISA MARIE LOUISE 21952 11973061991041578112722.4211MSANS
+7029VIERGE DANIEL 11953 51976091994112547019086.5111MSANS
+6315EPSOM LAURENCE SONIA 219531119961019951010668 9425.3111MSANS
+8330GUISE VERONIQUE 21954 91975071974071215710279.3211MTECHN
+4963GUILLOTIERE ROBERT 11954 21977071992072350617727.3311MSANS
+9223MARCHAUX JEAN PATRICK 11955 91975031991071578112722.0711MINFOR
+8081JAMBLES BERNARD 11955101980061995041249910472.1211MSANS
+2906DOUGLAS ERIC JEAN MARIE 11955 21978051994011369311287.7711MSANS
+3749MAGNAN PIERRE 11956 7197910199111 4134 0.8911MESP
+8176THENOT DIDIER 11956 71980021982102470018541.8311MSANS
+3392SIMON PIERRE 11956 61979021994041249910472.7011MSANS
+6393BERGERS LYDIE 2195611198901198604 8656 8146.4111.VENTE
+7181CABAUDRAN FREDERIC MICHEL 11957 21978061991102529918969.2211MSANS
+6568LANDEDA THIERRY PATRICE 11958 31979071993071471512025.9511MSANS
+0333DAPHNE CHRISTOPHE 11958 719820619810611131 9657.6211MSANS
+2504TREICH JACQUES 11959 51982051994081638413149.5711MALLEM
+3534KHRAIEF CHRISTIAN ANTONIN 11959 61981031995021433211714.7011MSANS
+9601PECLET GENEVIEVE 21959 119791019781010024 8999.7411MSANS
+2575BELLEVUE FRANCIS 11959101980051991091369311287.1611MSANS
+1679GRIGNAN GERALD 11959 71981081993081804514082.7211MANGL
+4039GOULAINE EDWIGE MARCELLE 21959 51979081993071471512024.9611MSANS
+3106YABBOQ CHRISTINE 21960 119910619900610024 8999.7011MANGL
+5771GONDS JACQUES 11960 21983051995011433211713.8911MSANS
+3478ATHOS FRANCOIS 11960 21983081989112090215980.7311MSANS
+3217MOINES FRANCE 21961 51982111981111326611017.1611MTECHN
+3318EPINAY DIDIER 11961 51982111995091450311832.0011MVENTE
+0241PROVENCALE CHRISTINE 21961 11984021983021249910472.6111MSANS
+7712JANVRY CHRISTIAN 11962 41986081995011373711328.3211MTECHN
+8426FILET GLORIA 21962 1199607199507 8656 8146.2911MSANS
+2991BOIRARGUES MARC 11962121982061990091326611016.1711CSANS
+4250FOREST ANGEL 11962 219830819950911131 9657.3911MINFOR
+0642GOGH ANNICK 21962 719960219930410668 9427.0711MSANS
+3978FONDETTES YUKIKO 21963 71984081983081527112374.5211MANGL
+1989BRANCION MARIE JOSEPHE 21963 51990041993081330611017.2911MSANS
+7919CERFS BRIGITTE 2196310199310199210 9597 8729.1711MALLEM
+6531DORMELLES PIERRE JACQUES 11963 41983041982041326611016.5111MSANS
+0104SALETTES FREDERIC 11963 219860519850511131 9658.8611MMICRO
+3391LAURISTON JEAN CLAUDE 11963 41983061991091326611017.6511MINFOR
+9918BOULEAUX DANIELLE 21964 8199505199405 8656 8146.1111MSANS
+9860MOREAS LOIC 11964 2199303199203 9597 8728.9811MSANS
+4629LEMBRAS MARC 11964 31988041989011527112374.8411MSANS
+8531SIGNORE CHANTAL 21965 4199607199507 7803 7680.8911MSANS
+5775GRANDS LINDA 21965 41988061987061249910473.6311MESP
+0548VALERIEN PHILIPPE 11965 71986041995041710713577.0111MSANS
+5728MUCHARISTA GILLES 11965 81985071991121288210744.9511MSANS
+0495ALIZES JEAN MICHEL 11965 61986071993111288210745.2211CTECHN
+3119GAMBETTA JEAN PIERRE 11965 519851119841110791 9502.2311MANGL
+7770NOTTET CHANTAL 21966 31985101984101288210744.2611MALLEM
+7186ILLIERS FRANCK 119661219921119911210024 8998.3111CANGL
+5800COLOMBIERES VERONIQUE 21966 71992081991081202810240.8611MSANS
+8549ROMAI PATRICK 11967 81987041993081450311830.5811MANGL
+2557CHAI CATHERINE 21967 9199201199101 7803 7680.9811.INFOR
+5044ROUSSILLON HIOU SHU 21967 51987111993031381911365.1911CSANS
+3101MARECHAL JACQUES 11967 91988071993011839014313.7211MANGL
+9282LEOPOLD ALAIN 119671119880519890910407 9271.9711MSANS
+1201PEYRERE PASCAL 11968 919881219871210407 9270.4211MALLEM
+7061TEICH PAUL 11968111994031993031267210588.7411MSANS
+5746HARRIAGUE GERARD 119681119900919890910407 9271.1911MANGL
+6486MAR ALAIN 11968 1199404199304 7590 7564.3511MSANS
+0773VICTOR JEAN CHRISTOPHE 11968 71994081993082107316096.7111MALLEM
+3494VILLARCEAU ISABELLE MARIE 21969 7199609199509 9894 8921.5911MSANS
+1068AMOUR DIDIER 11969111991051990051467711987.6711MINFOR
+1796ROCQUENCOURT FRANCK 11970121991091990091202810241.7011MANGL
+6197WRIGHT ANNE 21971 919930319920311389 9850.7011MSANS
+9660CYPRES GERARD 119711019900719890710024 8997.9011MALLEM
+8022ARCAY CLAUDE 11971 51992111991111202810240.1311MSANS
+3764CHEMIN FABIENNE 21971 219951119941110668 9425.6411MVENTE
+2448PINCHINADES JEAN CLAUDE 11972 21995051994051450311831.0111CALLEM
+1927VALESCURE PHILIPPE 11972 4199501199304 9597 8728.7711CSANS
+2074AZUR DIDIER 11972 51993111992111267210588.5311CTECHN
+9842BASTIDE ERIC 11972 5199312199212 9597 8727.9111CSANS
+5954SABLES ROCCO 1197212199305199205 9597 8728.1911CSANS
+0278PAIXENT JEAN MICHEL 11972121994101993101360611210.6011CSANS
+1572DAGNY BRUNO 11972 11993101992101360611210.2811MSANS
+4298AIZIER NICOLE 21972 219940719920111389 9852.4211MALLEM
+2321DESIRADE EVELYNE 21972 119910719900710024 8998.1311MTECHN
+6794CHEUVRY PHILIPPE 11973 9199207199107 9597 8729.9211CSANS
+8383ETIOLLES ANNE MARIE 21974 5199607199507 9894 8922.2111CSANS
+3562PORSCAVE GEORGES PASCAL 11974 1199508199408 7803 7679.9911CANGL
+6773GERANDO NICOLE 21974 51995051994071450311831.9711CANGL
+4226APPY BERTRAND GEORGES 11975 2199606199506 9256 8532.5611CTECHN
+1343SANS FRANCOIS 1197510199410199310 9126 8456.3911CSANS
+6979ANGLAIS NADINE 21975 7199304199302 8656 8145.5711.SANS
+5254BERLIOZ DANIELE 2197610199607199507 7803 7680.5011CTECHN
+0275FRIARD GERARD 1197610199608199508 7803 7681.4711CSANS
+3929CHATEAU VERONIQUE 21935 2199301199201 9085 8417.4612VANGL
+2917PASTEUR NICOLE 21935 31967071990011932314933.6012MSANS
+4491FALLIERE CATHERINE 21935 7199109199507 7677 7603.7312.ANGL
+4190CHRISTOPHE DOMINIQUE AUGUSTE 11935 31977111992101369311287.1912MANGL
+3826RAYON BERNARD 11935 319830819820811389 9851.7412DESP
+2841SEVERINE PHILIPPE 11936 6199409199309 8656 8146.7412MANGL
+6337FREDERIC CATHERINE 2193812199201199101 9085 8417.6312SSANS
+7355ANTOVAL JEAN FRANCOIS 11938101961021995043200223582.3212MANGL
+8990DETALOUX CLAUDIE 21938 71962071984102273717260.0812CSANS
+7576SEBASTIEN BERNARD 11939 51971121991071663613343.3712MCOMPT
+3808CABRIES ANNE LOUISE 21939 31973071985041834514275.9512VSANS
+3333BOSQUE JEAN YVES 119391219900919920310791 9503.1612CTECHN
+0851ORANGERS CHRISTIAN 11939 11973071992011433211715.2212MALLEM
+7226ROYA FRANCOISE 21939121962121983122470018541.7912MSANS
+4767DOMITIA VINCENT 11939 6198912198504 8656 8145.1112.VENTE
+1932LEROY FRANCK 11939101968101991011433211714.2112MTECHN
+4220GUERINIERE MICHEL 11940 11971041991011663613343.9712MSANS
+5021MONTLANDON RENE 11940 61973041993011369311286.6612MSANS
+3088ALTAIR NICOLE 21941101962031994122470018540.1712MSANS
+2222MILLET JACQUELINE 21941 21970051992072137316331.7812MALLEM
+6726HURONNERIE CHRISTIAN 11941101980031992011403411521.2912MANGL
+1951ROSIERE GILBERT 11941 91971031990011433211715.5412MSANS
+4436CHAVANNES MARIA LUIZ 21942 51968111991072137316332.6012MSANS
+9151CHATONNAY PAUL 11942 21969021990071433211715.6312MTECHN
+2643SOUSBOIS DOMINIQUE 21942 31962041988122470018540.2412MSANS
+6554SARCELLES JEAN MARIE 11943111963071993083200223584.0712MTECHN
+6672RENDEZVOUS FRANCOIS 11943 11969091990101663613343.6012MSANS
+3579RUISSEAU DANIEL 11943 31965011994011557012567.1812MANGL
+0484PAUL CORINNA 21943 61965121988062470018540.6612MSANS
+3880ROUVRES ERIC 11943 819931119950610407 9271.7412MSANS
+4665MONTEE JOSETTE 21943111968041991052470018540.5712MSANS
+3302VERS JEAN 11944111970061991011433211714.8712MANGL
+6749AUCUN JEAN CLAUDE 11945 11974071992041471512025.0212MINFOR
+6957RESIDENCE EVELYNE 21945 61971071991101578112723.4212CSANS
+1669CHEZY PIERRE 11946111976051993011369311286.0612MSANS
+1674AVENIDA SYLVIE 2194611198806198706 9511 8688.4112DINFOR
+6741FONDS GEORGES 11948 11977041994011249910472.1012MSANS
+4859ZORI CATHERINE 21948 81968111991021834514274.7012MSANS
+1314MINES JEAN MARIE 11948 31975061993071369311288.1812MALLEM
+5393PROVEN ANNICK 21948 9199309199209 8656 8145.0612.ALLEM
+9886FLAUBERT YOLANDE 21948 41968041995111684813422.3612MANGL
+8118JUGY JACQUELINE 2194912199504199404 8656 8146.6412MTECHN
+3693RETRAITE MARYSE ARMANDE 21949 9199201199101 7803 7681.4712.SANS
+7335PETITES MICHELLE 21949 71970101990112043315671.1512MSANS
+6896MERIMEE PATRICK 11949 11972031995011834514274.2412MANGL
+0173RESIDENCE HARRY 11949 71973061992101369311287.1412MVENTE
+7211LAVIGNAC MARIE ANNE 21950 6199307199207 7803 7680.0912.SANS
+6258DEAUVILLE SALIM PHILIPPE 11950 61975101993011684813423.4912MSANS
+6945BERANGER AGNES 21950 11970011989011621213034.1012MSANS
+6072LAZUEL CHANTAL 21951 11971051995041834514274.4412MSANS
+3233GRETZ PATRICK 11951 61981041993011608112917.2412MSANS
+3090VERTS THERESE 21951 21972101987072334017610.2012CALLEM
+4522POISY BRIGITTE 21951 41973051991011578112722.7012MSANS
+1211SAINTE PIERRE 11951 219940119940110407 9272.1312MSANS
+9697RUE NADINE NICOLE 21951 11977071991051608112915.7412CTECHN
+7336GRURY ANTOINE 11951111977051993041369311286.9212MTECHN
+4900BENARD JEAN LOUIS 11951 71973081992032470018540.1512MSANS
+0543BARETY JACQUES 11951 71977031993011369311286.8012MSANS
+4195GRENIER THIERRY HENRI 11951 71979011994111403411522.0912MALLEM
+6636BERANGER JEAN PIERRE 11951 619831219821211131 9659.0112MALLEM
+1282PALOMBES OLIVIER 11952 81979061995011621213033.3712MESP
+7146VENERIE YANN MAR 11952101981031992011249910473.4712MSANS
+8503GIRELLES MARTINE 21952 41993111992011202810240.1312MANGL
+9767ROUBLOT FREDERIC 11952 71976121991071578112722.3712MSANS
+1506BELLES SERGE 11952 1199301199201 8656 8146.7012.SANS
+5411MERMOZ BRIGITTE 21953 419910919900910024 8997.8112SSANS
+8001PANORAMAS JACQUES 11953111980031992101369311287.3212MALLEM
+8972NOGUES JEAN 11953121981091992011249910472.0112MSANS
+6536MARX EVELYNE 21953 7199201199101 9511 8688.8912MANGL
+3768LAMBRIGOT PHILIPPE 11954 51976071991012022115554.5412MSANS
+0662BRACQ GEORGES 11954121975111993121369311286.4712MSANS
+5304BETOUILLERE NORIKO 21954 71979061993041433211713.9212MALLEM
+4215PARA KARIN 21954 91979071992111608112915.5312DSANS
+2037FORGE LUCIEN 11955 21984021992111578112723.2412MSANS
+2945ABBEVILLE PASCAL 11955101983101993011471512025.4612MANGL
+2849NERTHE MARTINE 21955 21978091993101471512025.9212MSANS
+3488REVOL ANNE 21955111977071992011471512024.0612MSANS
+4431LAUNAY LOIC 11955101984091993011215710278.6912MSANS
+8724CASSIFLORE ROGER 11956 11976071985102397418036.3812MANGL
+0629FRANCOIS SANDRINE 21956 91977071995101881314586.0612MSANS
+4823RENARDIERE JEAN LOUIS 11956 41977031992111608112915.2912MSANS
+8808ENGHIEN MAX 11956 91977061992011471512025.6512MVENTE
+2432MICHELE ISABELLE 2195710199611199511 7803 7679.4512CINFOR
+7158CAPITAINE DOMINIQUE 21957 91978061993031608112917.0412MSANS
+6317COURLIS GUY 11957 21980051992121369311286.7412MSANS
+7608DESSUS SABINE NICOLE 21957121979031993041471512025.2812MSANS
+3637PEYRIERE FRANCOIS 11957 81979111995101911114778.2412MSANS
+9600HAKEIM THIERRY 11957 51981031986092256617146.6812MANGL
+1537RESISTANCE SERGE 11958 81986031991101868714506.0112MSANS
+3033PIERREDON JEAN LUC 11958 51980041992121471512024.7812MSANS
+9058ROYALES ALAIN 11958 31982081994041326611016.6912MSANS
+1074PONTILLARD JACQUELINE 21958 51981111980111608112915.8412MALLEM
+0339VERNOCE THIERRY 11958 81982041994041471512025.3212MSANS
+1325ZOLA CHRISTINE 21958 81978121993041471512024.9312MSANS
+6083HAIG MICHELE 21959 419791119781110876 9502.3712MSANS
+9351QUIHOU STEPHANE 11959 41988061992091450311831.2512MSANS
+9582LAENNEC COLETTE 21959 41983071982071326611016.0812MSANS
+5220RECOLLETS YVES 11959 51981011994011471512026.1512MSANS
+5140DAILLON DOMINIQUE JEAN 11960 31984071995081527112374.5812MANGL
+4446ELPHEGE PIERRE YVES 11960 419840319830811131 9657.2612MANGL
+0969BOUNIN PHILIPPE 11960 71983031991111616712995.2012MSANS
+2335CAILLOUP CHRISTINE 21960 11980091994071471512025.0212DSANS
+8339MER ISABELLE 21960111980101979101684813422.5712MANGL
+6061ESCALES JEAN MARIE 11960 51982051995121804514082.6512CSANS
+0818GATTIERES PATRICE 11961121983121989031527112373.0812MTECHN
+5053LARUE KARINE 21961 8199407199307 8359 7952.0712MSANS
+9219OPALINE EVELYNE 21961 61983111982111202810240.8412MALLEM
+6160LAURENT ANNICK 21961 51982091981091527112373.7112MANGL
+9514GARGAS NICOLE 21961 31981041980041684813421.4912MSANS
+2936KIPLING ERIC 11961 419830419830711131 9659.2212MTECHN
+3023BOREL PHILIPPE GILLES 11961 51982041981081608112915.8912MSANS
+5767BORGHERE PASCAL EMILE 11961 51985061989112090215981.0912MSANS
+9519SUISSES PHILIPPE 11961121985031984031608112916.4312MSANS
+7271BOUEL JEAN LUC 11962 21982071990101527112374.1612MSANS
+1969BIERE SIMONE 21962 3199404199304 8359 7953.2412MESP
+1388MOLIERE GERARD 11962 71987081988011527112374.2012MSANS
+5714NICOLO DIDIER 11962 11983071984121608112916.8112MCOMPT
+1039ATTILL ERIC 11962 91983081984091608112916.6412MALLEM
+4474SAUSSAYE MICHEL 11962101989011992121249910471.9712CANGL
+0108ORBESSON MIREILLE 21962 81983041989112090215981.4112MSANS
+8464DIJON JEAN LUC 119621219860219850210791 9503.7012MINFOR
+0217MAGDEBOURG PIERRE 11962 51986071995011373711327.8712MANGL
+5259VANOEL SERGE 11963 81984021991011868714507.8512CSANS
+6176GOETHE GILLES RAYMOND 11963 41983051993121326611016.5712MSANS
+3711SIGNAL KARIN 21963 6199407199307 9126 8456.8212MCOMPT
+5792ARCACHON SERGE 11963 519931019910710407 9271.9712MCOMPT
+2491OSSOLA ANNIE 21963 51993101992011202810240.2612CSANS
+5463PERE JEAN LUC 11963 819860319850310791 9503.8812MSANS
+0572ALSACE MICHEL 11963 219830619920111131 9658.0212MANGL
+5197LATECOERE BERNADETTE 2196411199201199101 7803 7680.2312.SANS
+3820SAUSSES MICHEL LOUIS 11964 31986061985061288210743.8312CINFOR
+3831CRESP BRIGITTE 21964 6199109199009 8656 8145.8412.TECHN
+7862GUILLEMARD GERARD 11964 619870419880910791 9503.2712MSANS
+3863TREMOILLE LAURENT 11965111989011991051249910473.0012MTECHN
+2269VERDUN DIDIER 119651019870219910110407 9271.4212MSANS
+4817VAUCLUSIENS JEAN PIERRE 11965101986051991091288210743.8612MVENTE
+8120BUSSIERE LYNDA 21965101988041994121450311832.2912MSANS
+9872ROTTEMBOURG PHILIPPE 11965 71985071991091288210745.6112CALLEM
+3157GEORGES MARC 11965 119870219860210791 9502.1412MVENTE
+5375GRAFFIANE ROBERT 11965 119870419861210791 9502.8912MSANS
+9838SELLE XAVIER 11966101992011991011202810240.9512MSANS
+4716BEG PASCAL 11966 61987061992041249910473.4212CANGL
+8142FONTENIL MADELEINE 21967 91990051993051723313653.6012VESP
+4631ROBINS ISABELLE CAMILLE 21967 61993111992011202810241.8112MSANS
+0875RESISTANTE ALAIN 11967 61988041994011249910473.1812MSANS
+7618JUANE JOSEPH 11967 919880919910210407 9270.4812MSANS
+6297QUINT MARIE CHRISTINE 21967 11989121988121202810240.8412MINFOR
+9234SAINTRY FRANCK JEAN 11967 619891019881010791 9502.8912MSANS
+0878MERIMEE GUY 11967 81990011989011467711986.9112MSANS
+7463VALETTE BERTRAND 11967 91987051994051381911366.9312MSANS
+1043RAMBOUILLET JEAN FRANCOIS 11968 81990071989071381911364.8712MANGL
+2350PANPYC CLAUDE 11968 91990111989111467711985.8712MSANS
+9468GUEPIN EVA 21968 41989051988071949415051.6812MSANS
+4690VERLIN YVES 11968101989111994011249910473.4712MINFOR
+4832BAYARD CORINNE 21968 21990111989111202810239.8312MSANS
+2141CHATAIGNIER CATHERINE 21968 519940919930411389 9852.2212MMANAG
+2439AIGUILLE CATHERINE 21968 9199407199307 8359 7952.2512MVENTE
+2895PASSAGE PHILIPPE 11968111990031989081381911366.2212CSANS
+6720ANTHEOR VINCENT 11968 21988051993041249910472.6412MSANS
+4877AUBIN PATRICK 11968 71990011989011202810239.7812CSANS
+7975GUITTARD ISABELLE 21968121993121992011202810240.1912MSANS
+3884DESMOULINS ANNIE 21969 2199510199410 7803 7680.5012MSANS
+4326CASSIOPEE JEAN PIERRE 11969 21989031988031249910473.5612MSANS
+9844DAUDET ROBERT 11969121993101992101330611016.3012MALLEM
+1235TRANSIT CHRISTINE 21969101992031991031420611639.2212CINFOR
+9148CARTIGNY TONY PIERRE 11969 81994091993091450311830.6212CMANAG
+1898VEUVE BERNARD 11969 119891019881010407 9270.6912CALLEM
+4856BELIER JEROME 11970 11990111992011381911365.6212MSANS
+3215BASTILLE GILBERT 11970 219921019911010024 8997.8612CSANS
+1681VAUCHAMPS THIERRY 11970 319940419930111389 9852.2412CVENTE
+0330GIRAUD DIDIER 11970 319940319920110024 8998.7112MALLEM
+2920FONZERI PAUL 11970 81993061992061267210590.0212MTECHN
+3390CLANS LOUIS 11970 51989111988111539912451.9712CSANS
+7148OISEAUX FRANCE 2197011199004198904 8656 8145.6612.MANAG
+2817GAIRANT MONIQUE 21970 319910719900710024 8997.9112CSANS
+8162VOLONTAIRES MONIQUE 21970 5199410199310 7590 7564.3812MSANS
+9567LEGUMES CHRISTINE 219711119950419940110668 9426.0312MANGL
+2435BRESSON SYLVIE 2197112199007199101 8656 8145.9312MSANS
+0955BEAUGENCY LAURENCE 21971 1199206199106 9597 8729.9712CSANS
+4021VICOMTE ROLAND 11971 119900619890610024 8998.0112MINFOR
+7963MONTESQUIEU HENRI 11971 51993081994011249910473.0512MVENTE
+9955WAY JEAN MARC 11971 1199305199205 9597 8728.0112CSANS
+1833INKERMANN LAURE 21972 919930519920511389 9851.3912MANGL
+4630CHEVREUILS CHRISTINE 2197312199306199206 8359 7952.0112.TECHN
+3273MIREILLE ALAIN 11973 819931119921111389 9851.5512CMARKT
+6842DARONNE ALAIN 1197311199307199503 9597 8729.2212CINFOR
+5953BOURGEOIS BRUNO 11973 21993081994011202810241.9212MSANS
+3467PONTIER ALAIN 11973 619930919920911389 9850.5612MSANS
+6507MAYVILLE CHANTAL 21974 1199208199302 8656 8146.4112MANGL
+4060HALLEGUEN MICHEL 11974 11993121994011202810241.7012CSANS
+8760RAGUINOT GHISLAINE ROBERTE 21975 1199507199407 7803 7681.4412CSANS
+1454NEUF GILLES 11977 9199606199506 7803 7681.3512CSANS
+2092BRASSAURIS MICHEL 11931 91956101978013327724477.6013VSANS
+8552VIOLET DIDIER 11933 4198810199501 8827 8263.5613.SANS
+6589DEVENCON BERNARD 11934111955111993103417325098.4513MSANS
+7036PERNETY FREDERIC 11934101961111987102273717260.4013MINFOR
+7079CHARDONNERETS LAURIS 11935 31978071992011403411520.5313CINFOR
+9868JAURES MICHEL 11935 51970051992011403411521.2013MSANS
+8365HELIOTROPES LISE 21935 11963021991072273717259.8613CSANS
+7450POISSON CHANTAL 21936 91968111989011621213033.5613MINFOR
+9779CONSOLACAO NATHALIE 21937 3199401199201 9085 8417.3613VSANS
+8202DIANE JACQUELINE SUZANNE 21937 61972041989101621213032.8713VESP
+5521LAZURE RAOUL 11937 31966021995011557012567.6313MTECHN
+7843NOM ETIENNE 11938 1199001198510 8656 8145.6213.SANS
+7282IRISASCO ERIC STANIS 11938121958081988013844228045.3713MSANS
+3124VIVIER GEORGES 11939121967011994011663613342.8513MSANS
+1445GAUSSADE FRANCINE 21939 11967121992012137316332.1113MSANS
+5399GROSSETI JACQUES 11940111969011990011433211714.7913MSANS
+7177MONTSEGUR DOMINIQUE 11940 2199206199106 8656 8146.8313.ALLEM
+4475SOLEIL JEAN CHARLES 11940111962041993043417325097.6413MINFOR
+6615GRAVELLE CLAUDE 11940 419880319870810407 9270.3013MESP
+5880OR LILIANE 21940 8199301199201 8656 8147.0113.ANGL
+0756RENARD PATRICK 11941 71960041991013327724477.7213MSANS
+9016SIMON JACQUES 11941 81973061992101369311288.1813MANGL
+4931AIGNAN SALOMON 11941121972111992011403411520.2913MSANS
+7676ROUX DOMINIQUE 11941 81969121990011433211715.5413MSANS
+3434NOUVELLE GUILLAUME 11941 31970061991011433211715.4713MSANS
+1569MOULINS CATHERINE 21941 31993051993101249910472.9113MSANS
+4399SOLDATS ANNE 21941 71963091994011932314934.0013VSANS
+4929MANOUCHIAN RICHARD 11943 41979011992011249910473.3213MALLEM
+5101CLOT PATRICK 11943101977041995011578112723.1813MSANS
+1399PLANTES MEDERIC 11943 61962111991072273717261.5413MTECHN
+4835LECHIAGAT MAURICE 11944111963121993082922321683.6013MSANS
+2862CASTELROC PHILIPPE 11944 81968091988062947921840.0613MTECHN
+5852PUGET JEAN LEO 11944101971011990032022115554.8113MSANS
+4369BOUCHER LUCIEN 11944 51976051991012022115555.3913MALLEM
+6206VERANE COLETTE 21945 11975101987031684813422.3113MSANS
+2842ROND THIERRY PIERRE 11946 11967121993013844228045.1613MANGL
+8250JULIEN MARIE PAULE 21947 4199409199309 7803 7680.0013MINFOR
+4160ALLUES DANNY 21947101978071993012529918968.6013MSANS
+8190BUT CLAUDE 11947 71982041994011471512024.0813MANGL
+9979FLORALE STEVEN 11947 21975041992101369311286.7813MANGL
+5458BEYNAC PIERRE 11947111972031994011663613343.8813MESP
+5735CONDE THIERRY HENRI 11947 51976041995011578112722.5213MSANS
+6972BIRAC DANIELLE 21948 41973071991071578112722.1913MSANS
+8328MAC LAURENT ROLAND 11948121977031993011369311287.1013MSANS
+8020COMMODORE JACKY 11949 21977031993071369311287.0713MSANS
+9492BOURGADE FREDERIC NICOLAS 11949 91972081986092470018540.6013MANGL
+7494HAM JEAN BAPTISTE 11949111979041993081369311287.1913MSANS
+2684LANTERNE JEAN PIERRE 11950121975041991071578112723.1813MSANS
+8110LACOUSSIERE MONIQUE 21950 71993101992011202810240.4013MANGL
+4050HERBILLON FRANCOIS 11950111971051991012470018540.6113MSANS
+0115ACHILLE JACQUES 11951 41976021992091834514274.9313MSANS
+6913MADAGASCAR FRANCOISE 21951 81977101993041471512024.8713MANGL
+9541FARGE FRANCOIS 11952 91984031994011433211714.8813MANGL
+1147VERTES GEORGES 11952111979041991011608112917.2413MANGL
+3720FRESNE PASCAL 11952 91973021992082022115554.4213MALLEM
+3427LYAUTEY SOPHIE 21952 91973051991011578112723.4213MALLEM
+6348TOURELLE BEATRICE 21952 51972061990011621213034.1513MANGL
+6291MONTMERY MICHEL 11952111978091995011578112723.5413MSANS
+9048VILL YANN 11953 51981021993011369311286.6913MSANS
+2556GAGNEUR MARTINE 21954 3199611199511 8359 7952.9113MTECHN
+5041GALAND MICHELE 21954121981071995071433211714.0113MSANS
+6421OUERRE CHARLEY 11956111982071993011249910473.1113MESP
+8090BALISE JOEL 11956 61986011995011373711326.7113MSANS
+6312RASPAIL CAROLINE 21956 619940619920111389 9852.5113MSANS
+6598RUBENS JEAN MICHEL 11957121976121992011578112721.6513MMARKT
+7944MAUBEUGE JEAN ALAIN 11957 11982031981031326611016.2913MMARKT
+8793MOREAU BERNARD 11957111985051988031288210745.0413MANGL
+8404CHEVERNY OLIVIER 11957 51983081995011578112722.8713MSANS
+1860CHAMPIGNY JEAN HUGUES 11957121978031993051608112916.7213MSANS
+9938CHARENTON PASCAL 11957 21981061993011471512025.2513MSANS
+0001TASSIG ARMELLE 21957 71982031981031288210743.8113MSANS
+6966GARE MARIE THERESE 21957 41979101993071471512025.8213MINFOR
+5849ELOI FRANCIS 11958 21983051993011215710279.9913MALLEM
+0222GERTWILLER ROLF MIC 11958 61983061995061894014661.1213MINFOR
+9106MAISON JEAN CLAUDE 11958 11978021992051471512025.5613MSANS
+3045BATISSE PHILIPPE 11958 51977121992012175716600.9413MVENTE
+6087HUCHA ANNE NATHALIE 21959121981011988112090215981.6313MSANS
+7732GENETS JEAN DENIS 11959121986061985061288210745.3913MSANS
+8156BRASLES BRUNO ROGER 11959 81984041995071433211714.0613MSANS
+4809GENEVOIX ALAIN 11960 219810919810111389 9851.6613MTECHN
+2618BOCOUMYAJOUR PHILIPPE 11960 41980061992092090215981.1813MSANS
+1786TREVARESSE FRANCIS 11960 21981091994011471512025.5013MSANS
+7909CHARITE CATHERINE 21961 11985041984041527112373.8513CESP
+7849BUCHES COLETTE 21961121985051984051527112373.8513MSANS
+7624JOUVENES CATHERINE 21961 1199504199404 8359 7953.5113MANGL
+2428DUCOS FRANCE 21961 1199309199209 8656 8146.1613.ALLEM
+4640FINLAY JEAN PAUL 11961 319830419841011131 9658.3413MSANS
+0498MONET ANDREE 21961111983081982081608112917.0013MSANS
+3902ISTANBUL DENIS 11962 519840619830611131 9657.5313MINFOR
+8720LOU ALBERT 119621119880819870810407 9271.3413MALLEM
+9086CARNOT HENRI 11962101990111989111450311830.9813MSANS
+4105CHAMINY YVES 11962 21982051987041608112917.1013MVENTE
+7566SAINS CHRISTIAN 11962 31983091993051527112372.8113MSANS
+2552FOCH FRANCK 11962121986061990111288210745.8113MSANS
+5248VERONESE PATRICE 11962 219890119880210407 9271.9913MSANS
+0127GRENETA BEATRICE GENEVIEVE 21962101984071983071288210743.8613MANGL
+6150MAUREL DOMINIQUE 21962 11984071983111608112915.0813MVENTE
+7881CHAILLEY ISABELLE 21963101985091984091288210744.4413CSANS
+3227POINT COLETTE 21963 31989031988031249910471.5813CANGL
+1094FERRAT ANNE LORRAINE 21963 91983061982061326611017.8313MSANS
+0209MONTALEAU PASCAL JEAN 11963101983061986111326611017.3413MTECHN
+6991COLOMBIER MARCEL 119631119870519880210791 9502.8913MSANS
+8764PALMAS MARTINE 21963 81994051993051267210590.2913MTECHN
+2275FANGADE ELISABETH 21963 51984021983021288210744.8513MTECHN
+0866SECRET MIREILLE 21963 71991111990111249910472.0313MSANS
+0133TORREPORTO CHRISTIANE THERESE 21963 6199302199202 7803 7680.4513.SANS
+1651VALERE VINCENT 11963101987081991071288210745.9413MANGL
+0332FRER CATHERINE 21964 7199407199307 7803 7681.2913MINFOR
+1313ARPHY FREDERIC PASCAL 119641019870319860310791 9502.9813MSANS
+7694BANNIERE GERARD 11964101987041992111288210745.9913MSANS
+2808BOULIE ISABELLE 21964 41985051984051450311832.6913MSANS
+9520ERMONT VERONIQUE 21964101993111992011202810241.1313MSANS
+7726BUTIN JEAN YVES 11964 6198810198707 8656 8145.0313.COMPT
+9487MARECHAUX ERIC JACQUES 11964 21988051987051450311830.9813CSANS
+7195ARENE CHRISTINE 21964 71994101993101249910471.7613MINFOR
+3536HAIE NATHALIE LUCE 21965 71985121984121249910472.4713MSANS
+0845CHARLEMENTINE DOMINIQUE 21965 519940419920111389 9852.7413CTECHN
+8486CANADEL SUZANNE 21965 41985121984121288210744.4113MALLEM
+5385ROZOY TRISTAN 11965 31987071992031450311830.8313MANGL
+7086BEAUME ALAIN 119651019860619850610791 9502.5313MSANS
+2621LOTISSEMENT STANISLAW 11965 51986051990111616712995.9313MSANS
+9449POSTE MARIE CLAUDE 21965111989031988031202810241.5713MSANS
+1109PERRAULT CHRISTIAN 11966 81986041994041450311831.3713MSANS
+4280BEGUDE MARTINE 21966 91986041985041288210744.1913MSANS
+1973ESPRAT JEAN BAPTISTE 11966 919900119900510024 8997.9113MTECHN
+6164UNIVERSITE VERONIQUE 21966 41985121984121288210744.1313MSANS
+9407THIERRY GUENAELL 21966 91990091989091638413150.7913CALLEM
+6809PERCEY PHILIPPE 11967 519930819920811389 9850.5313MTECHN
+9633GILANDIERE ANDRE 11968 61993081994082256617146.7213CSANS
+7128PERSPECTIVE PHILIPPE 11968101991101990101467711987.3613MSANS
+0709AMYOT ISABELLE 2196810199603199503 7803 7680.2113MSANS
+9931CIVADE PIERRE 11969 51991111990111467711986.6213MSANS
+1496FERME STEPHANE 11969 519890919880910407 9271.7913CTECHN
+4802JAURES MARIE CLAUDE 21969 21994011992011202810240.8213MALLEM
+5721VIEUX ERIC 11969 41989061991021249910473.5413MSANS
+4763PINTA ALAIN GEORGES 11969 919921019930210407 9270.6513MSANS
+9618PASTOUR CHRISTINE 21969 11994081993081206910240.4613CSANS
+3207PAQUERETTES MARIE CHRISTINE 21969 619940619930410668 9426.0813CSANS
+0667FOURCHES SYLVIE 21969 719920719930411389 9850.6213MSANS
+8437ORANGERI DANIEL 11969 31989041992121249910473.1513CSANS
+4233LEBLANC CHRISTIAN 11970 419910119900110024 8999.3113MANGL
+2043RAVEL FRANCOISE 2197010199301199201 9597 8728.8913MSANS
+4748REDONDO CHRISTINE 21970111994081993081206910241.3013CINFOR
+6379ROUGE CATHERINE 21970 11994011992011202810241.7213MALLEM
+4507ALBAN CATHERINE 21970 91993011991011202810240.9813MSANS
+8424FRANCE JEAN PIERRE 11970111992081991081202810240.9913CANGL
+3409FLEURUS BRIGITTE 21970 9199009198909 8656 8146.6813.ALLEM
+8040ROY PATRICK 11970 41991111990111839014313.7013MSANS
+4985CUTTE MICHELE 21970 419940519920111389 9852.6013CANGL
+0589MARTIN FABRICE 11971 11990091992021202810241.9013CANGL
+2531ROI MICHEL 11971 41994011992011202810240.4413LSANS
+0686RENAUDIN JEAN FRANCOIS 11971 51990091993121202810241.1213CSANS
+1223ROUDIL CECILE MARIE 219711019921019911011389 9852.5913MVENTE
+0603THORETON GUY 11971 7199605199505 7803 7681.0813MSANS
+4480WAGNER YVES 11972 31991091994011249910473.6313CSANS
+7873VALERY NORBERT 11972 319921019911011389 9852.0613MSANS
+0068LAC CATHERINE 21972 419940519920111389 9852.2713MSANS
+6496BEL CATHERINE EMILIENNE 219721119920219910211389 9851.3413MESP
+4096XAVIER THIERRY 11973 31995121995101288210745.8513CSANS
+0302FRANCH WILLIAM 11973 8199502199402 9894 8922.8613CSANS
+1195GUILBERT ELISABETH 21973 819930319920311389 9851.4113CSANS
+8888LEGER BERNADETTE 21973 7199409199309 9126 8455.9813CSANS
+9415FONTANEY MARIE PAULE 21973 4199404199304 9894 8922.4113MSANS
+8921ROSA SUZANNE 219741019940819930810668 9426.3413CVENTE
+1867BASSE JOCELYNE 21974 6199404199304 9894 8923.1713MSANS
+1798RUSSE THIERRY 11974 4199401199301 9894 8923.3113CANGL
+8684CHEM BERTRAND 11975 6199407199307 7803 7680.3913CSANS
+3841UTRILLO JACQUES 1197611199506199406 7803 7681.1613CSANS
+6399ABEILLES RENE 11934 7199207199107 9511 8687.5114MSANS
+1983PETUNIAS MICHELLE 21935 1199208199501 7803 7680.2614.SANS
+0006DOMONT YVES 1193511198810198509 8656 8145.3914.ALLEM
+6326JATELIER CHRISTIAN 1193511199107198509 8656 8146.0914.ANGL
+5224MAGNOLIAS MICHELE 21936 31962111991072273717260.2614MSANS
+1839ANSELME REMY 11937 21963121994011557012567.7714MSANS
+6250ASPREMONT JEAN LUC 11937 91968081990071433211715.6514MALLEM
+2328AGE MARIE HELENE 21937 81966071995102017815518.0914MSANS
+1178NAPOLEON HELENE 21938 61967071990071578112723.2714CANGL
+9417SOUTRANO MICHELE 21938 11963021963051288210744.3714MVENTE
+9022GATINES CHRISTIAN 11938 11973071992011403411520.0814MINFOR
+6359LANCHY SOPHIE 21938 8198811198511 8656 8146.3414.ALLEM
+4141JASMINS PIERRE 11938 81976071992041403411521.0714MANGL
+7981NOUVELLES PHILIPPE 11939111968011993011557012566.8214MTECHN
+1626VEUR LOIC 11939 11982011993011215710279.2314MANGL
+4677ORMEAUX CHRISTIAN 11939 91963121994011557012567.6614MSANS
+0263ASPHODELE STEPHANIE 2194010198807198707 8656 8146.5514.ANGL
+8254CONNETABLE JOELLE 21940 8199509199409 8656 8147.1714MINFOR
+9870RESISTANCE PHILIPPE 11940 61967071993061834514274.5714MSANS
+4388ORBAY PATRICK 11941 41965051994011557012568.1314MINFOR
+0023ROUVIGNARGUES CATHERINE 21941101963011994042666519900.9114MMARKT
+9628BROOKE ROGER 11942 11962021991072273717261.4714MMARKT
+4240NUMANCE ANNIE 21942 71963031992012273717261.0014MANGL
+3342ROMPU FRANCIS 11942 61962031994113200223583.9014MSANS
+4773HA DIDIER 11942101961091994033200223583.1214MANGL
+2795ECHO BRIGITTE 21944 419941019930411389 9852.4114MSANS
+4188VISONNIERE MARIE CHANTAL 21945121972101971101249910473.7414MINFOR
+0739VALLES JEAN FRANCOIS 11945 71966081995042666519900.3614MSANS
+4899COURBANIERE DIDIER PIERRE 11945 31972061991011663613343.1614MESP
+7972FORET MARC 11947 41971071991011433211714.9114MESP
+1272CIGALES JEAN DOMINIQUE 11947101976101991011369311286.2114MTECHN
+6191HUGO MARLENE 21948 91973101991071578112722.6114MSANS
+1802CAVALIER PHILIPPE 11949111972021992092256617145.2914MALLEM
+3695JEANNE CHRISTOPHE 11949121977081992101471512024.1514MESP
+9900ANTOUNE FRANCE DOMINIQUE 21949 31972121992012017815516.8314MSANS
+0012MAUREILLAS RAPHAELE 21951 51971071988052334017609.1514DSANS
+9378VERDIERS CLAUDE 11951 41980031992061369311287.0114MSANS
+8265POUSSIEU DOMINIQUE 21951 81980061988052090215979.7614CSANS
+3423MORIGNY LUC 11952111979061992011608112916.7214MSANS
+7925FOURNIER JEAN PIERRE 11952 21976021995061608112916.2814MSANS
+2966ANTIBES JOEL 11952 41975101994041894014661.7814MANGL
+7905BOISSONADE FRANCOIS 11952 51990031989031288210744.1914MINFOR
+2700LERINS MURIEL 21953 91974011973011288210745.5214MTECHN
+6213TOISON JACQUELINE 21954 21974081982041527112373.3514MSANS
+1204TRIANON ANNICK 21954 3198801198511 8656 8146.6514.SANS
+7183CABRIER MARIE FRANCE 21954111974071979112470018541.1114MSANS
+0005BOILEAU FRANCOIS 11954 61974051990052175716601.9514MSANS
+7845LARGADES NOELLE FRANCOISE 21955101977021992011471512024.8714MSANS
+3059BLEUETS MAURICE 11956 81980071995011471512025.7014MSANS
+3868IORANA PATRICIA 21957 2199201199101 7803 7680.0314.SANS
+4327MARTILLE CATHERINE 21957101984091983091288210744.6414CSANS
+3448FAREL MICHELINE 2195712198805198603 8656 8145.9314.SANS
+6045AIGUELONGUE MICHEL 11958 71983081992061527112374.8114MANGL
+3104GAIRAUT JACQUES 11959121987021989051288210743.9914MSANS
+6864SCHMITT DENISE 21959 3199201199101 7803 7680.3414.ALLEM
+3805OUEN GERARD 11959 91980061994011471512024.5714MANGL
+7188SOL FRANCOIS 11960101981051991011868714507.4514MTECHN
+7952CABRO MARIE CLAUDE 21960 4199504199304 9126 8456.3314MTECHN
+3834JEANPIERRE FRANCK 11960 21981091987011684813421.4614MSANS
+2909BLERE PHILIPPE 11960 31983041993121326611017.6114MSANS
+8259VALOIS NATHALIE THERESE 21961 2198810198603 8656 8147.0114.SANS
+9506FORESTIERE ANTOINE REMI 11961 719821119811111131 9658.0714MSANS
+5269BIZET JEROME 11961111985041993021433211714.1514MESP
+5031CORBIERES PASCAL 11961 21982121993041326611016.6214MSANS
+0735MONTAIGUET ANNE MARIE 21962 819960619940111389 9850.5614MANGL
+0633BULLY LOUISE 21962 91991121994061202810241.5214MANGL
+7250SERMET XAVIER 11962 41985061986011527112373.0414MANGL
+9656SARRAMEA ANDRE 11962101983081994091868714507.1614MALLEM
+8263MAISONNEUVE MURIEL SOPHIE 2196212199404199301 9597 8727.8714MSANS
+8851BERGES MARTINE 21964 2199406199306 8359 7952.5514MSANS
+7351NUNGESSER PHILIPPE 11964 71988111987112188616680.9314MSANS
+3681LOOZE BRITTA 21964 21993121992011202810240.9114MESP
+1581VALLAGON MURIEL 21964 31986041987091527112374.1214MANGL
+0821BRANLY YVES MARIE 11965 51986011993041288210743.8714CVENTE
+3591MANNA CATHERINE MARGUERITE21965 7199407199307 8359 7952.4314MSANS
+7792INSTIT PHILIPPE 11965 619870719870410407 9271.4114MSANS
+5791RADEGONDE MARIE LOUISE 21966101986041985041168610048.4714MSANS
+0301ESTERELLA ANTOINE PAUL 11967121989021988021450311832.0014CSANS
+1872TOUL FRANCIS 11967 919900419900910407 9271.3814MTECHN
+5651MURIERS CHRISTOPHE 11967 31989101995071249910472.7414CANGL
+0527REMPART DENIS 11967111992071991071330611016.9614MALLEM
+8590TOURNEFORT CHRISTIAN 11968 6199410199508 9126 8456.6614MANGL
+5968PRESBYTERE DOMINIQUE 21968 21992011991011202810241.8414MSANS
+8374AUMALE JACQUES 11968 819890419880410407 9270.6614MSANS
+7416BREGUET DANIEL 11968 21987061993051381911365.2614MSANS
+0198MARINES MARTINE 219691119920419910411389 9852.0214MANGL
+8772GORGE MARC VITTORIO 11969121989091994011249910472.7814MSANS
+9157LEON JEAN CLAUDE 11969 81989061995101249910472.6114MSANS
+3588LUCIEN DANIEL 1196912199403199303 9597 8727.9914MSANS
+5266COUTANT DOMINIQUE 21969 2199502199402 7803 7680.0914MINFOR
+6967CONTI GILLES 11969 9199612199512 7803 7680.6614CSANS
+4277COLLONGES MARTINE 21969 2199112199012 8656 8146.3814.ALLEM
+6780ERIK SABINE 21970 819910619910411389 9850.6214MSANS
+9400LONGCHAMPS MARIE CLAUDE 21970 71990041989041202810241.6714MSANS
+0861ROUMEGONS GERARD 11973 81996101995101288210744.8514CANGL
+1149MOULIN MYRIAM ELISABETH 21974 9199501199401 7803 7681.2514CINFOR
+8134AJOUPA PHILIPPE 11974 3199508199408 7803 7680.5314CANGL
+5009LEVIS FRANCOISE 21974 6199506199406 7803 7679.6914CCOMPT
+5993FROBERT ARN 11975 7199503199403 8359 7951.9414CANGL
+7536ORNE BEATRICE 21976 3199602199511 7803 7680.3214CSANS
+0584LISON PATRICK 1197611199610199510 7803 7679.2814CSANS
+9645TESTE ALAIN YVES 11935 31964091994011557012567.6215MANGL
+4415CROIX GUY 11936 61966081981072022115555.5015MANGL
+2212BAC JEAN PHILIPPE ANDRE 11937 41972051992011433211715.3215MSANS
+9432ULYSSE JEAN MARC 11938 91960121988062947921840.2015MINFOR
+8124CHAMIGNY STEPHANE 11938 2199001198604 8656 8145.1215.SANS
+7324MOLITOR ROMAIN 11938 41962111992032572519240.6415MTECHN
+8862CONDAMINES NICOLE 21938 41960111976012470018540.4715MSANS
+2089ALLEN DIDIER 11939 81972041990011403411520.5715MSANS
+3194COUTURIER DENIS 11939 11963101994011557012568.2615MVENTE
+0835VALMANTE DENIS 11939 21959051991113327724475.7015MINFOR
+8646MESNAY ANNE GENEVIEVE 21939 51967011994091834514275.5215MANGL
+3426GAMBETTA ISABELLE CECILE 21940 7198912198603 8656 8146.0515.COMPT
+0422VAUGAILLERES CHRISTIAN 11940101963111988011663613343.0315MSANS
+0838ORVANNE PATRICK 11940 31962061993082922321683.8515MSANS
+4067OUTREMONT PATRICK 11940 31962031987012022115555.6215MSANS
+0744GRANAGHIU DIDIER 11940 81972021995041834514276.1015MVENTE
+9034TREILLE MARIE JOSE 21940 11962051984062470018540.8215CSANS
+5214BART MICHEL 11940 21964071991072273717261.7615MALLEM
+3460SERENA MARTINE 2194011198810198604 8656 8146.0515.ANGL
+3601MONTJEAN DOMINIQUE 21941 6198805198610 8656 8146.2015.SANS
+9277ELNE LAURENCE COLETTE 21941 31968071990011621213033.3215MSANS
+0325PLATANES CHRISTINE 21942121966081995091834514275.8315DSANS
+9878GAUTIER MARYVONNE 21942121972101989011621213033.2915VCOMPT
+9699GERVAIS JACQUES 11942 11966101992011663613343.4715CSANS
+6148GATINAIS FRANCIS 11943101969101992082470018541.2815MSANS
+4347MONTFRAY CLAUDE 21943 71962121991072273717260.0115MANGL
+4710VIRY GUY 11943 51970051991011433211714.3715MALLEM
+0354BENIGUET MARIE PASCALE 21944 41965021993111471512024.3315MINFOR
+6561LEBAS HELENE 21944 21964071994011932314934.2715SSANS
+1882BARBES JEAN MARC 11945 9199309199209 8656 8145.8915.SANS
+0226FERRIE CLAIRE 21945 4199207199107 8656 8146.7315.SANS
+6166BARGUE FRANCOIS 11946 61972071991101433211715.4515MESP
+9007CLAYE PHILIPPE GERARD 11946 21970041991011433211715.0915MALLEM
+9314GRILLONS MARTINE MONIQUE 21947121969031981122470018542.1015MSANS
+2474RODIN ERIC JACQUES 11947121972121991071621213034.1315MSANS
+0379ARGENSON BERNARD 11947 61971121991082470018541.2915MSANS
+1358SEILLE JEAN PAUL 11947 31971021993061834514274.8915MTECHN
+0628GOURGAUD PHILIPPE 11948 11969051993082666519901.0015MTECHN
+1939DISQUE ERIC 11948 41977031991011578112723.4215CTECHN
+1605FRESNES ALAIN 11948 51975061991011684813422.8115MSANS
+9214CHAMPS BRUNO 11948111973081991013417325098.6515MSANS
+5666NOVEMBRE JEAN PHILIPPE 11949 41971101994013417325097.9315MSANS
+0720MOINES GUY 11950121978011994031471512025.3415MSANS
+8674IMPERIAL JEAN CHRISTIAS 11950 21970041989111932314935.4315MINFOR
+8739HUGO MICHEL 11950 21974041992011578112721.8015MSANS
+9090CYR CATHERINE 21950 7198908198808 8656 8145.3915.SANS
+8528EMERAUDES BERTRAND 11951 61979061992011249910472.0615MSANS
+0912ALBERES MARTHE 21951 41974051991072137316331.3315MTECHN
+1664BENASY DOMINIQUE 2195210197206197106 7252 7486.2815MSANS
+5853ROI CHANTAL 2195210199112199012 8656 8147.0615.SANS
+6634DUMAS BRIGITTE 2195210199509199409 7590 7562.6315MESP
+9376LOUVRE SABINE MARIE 21952 51975051995021433211713.5315MSANS
+8735BYRON MARTINE 21952 51973071991071578112722.0715MALLEM
+1663PLANESTEL CATHERINE 21953 11974101993042017815517.2015CANGL
+5646ALEXANDRE MARIE CLAUDE 21953 11975071991091578112723.4215VSANS
+6614GLYCINES PIERRE 11953 71978101994031471512025.7215MSANS
+8773CLEMENCEAU ROLAND 11953 81974031991012334017609.3715CTECHN
+2191HEBERT PIERRE 11953 21972081992072043315669.2915MSANS
+7378THOMAS PATRICK 11954111982051994011433211715.6315MSANS
+6161HASTIGNAN ANNICK 21954 31980071994041471512025.0115VVENTE
+6895VALETTES ANORE 11955 31979061993121369311288.2415MALLEM
+8499ATHIS JEAN PAUL 11955 11977081976081834514274.8015MSANS
+8271SPORTS PAQUITA 2195612199112199012 8656 8147.0115.ANGL
+3980RAPHAEL MARLENE 21957 61980041979041684813421.6715MINFOR
+9964MUGEL PATRICK 119571119821219820311131 9658.4715MSANS
+0593GRACE JEAN MARC 11957 41977121992021471512024.5615CSANS
+2905EXEMPT PATRICIA 21957 3199201199101 7803 7680.5915.ANGL
+5492PERRIERE PASCAL LUC 11957 11983121994041894014662.7215MSANS
+7685SURMELIN JACQUES 11957 5198109198812 4864 2.1915MSANS
+3608ROOSEVELT FLORENCE 21957121980111994071471512024.5315MTECHN
+5451TULEU JEAN LOUIS 11957111977121994011621213033.1015CSANS
+4681VERIGNON JACQUES 11957 51984051995071433211714.1615CSANS
+1478RESIDENCE CLAUDE GUSTAVE 11958101982091995011433211715.7415MSANS
+0355ROSAIS MICHELINE 21958 81978061992051471512024.1515MANGL
+4981CHRISTOL BRUNO 11958 11978061995041249910473.5415MSANS
+6921MATRA FERNAND 11958 11978051993011471512024.4215MESP
+2956LAPEYRERE PHILIPPE 11958 11977071985102397418037.4715MANGL
+8126CYPRES JACQUES 11958 41983081982081608112917.1515CALLEM
+5587BERENGUIER JEAN PASCAL ROGER 11959 81981061993031894014662.0615MSANS
+2962CAYES CAROLINE 21959 61981051995041433211713.9215MESP
+5994GRASSE PHILIPPE 11959 819820419811211389 9851.6915MALLEM
+0767SOUCHET MARTINE 21959 91983011982011249910471.9215MTECHN
+1353AGEN JEAN MARC 11960 319810319800311131 9657.6515MANGL
+9232GROULLES ANTOINE JEAN 11961 31993101992011249910472.5715MALLEM
+3855BAUMES JEAN LUC 11961 41982111994031527112373.1015MANGL
+9369HELBRONNER VERONIQUE 21961111994031993031202810241.5715MINFOR
+4341OLIVIER CHANTAL 21961 31981101980101684813422.3515MSANS
+5305DAME ANNIE 21961 51981071995071433211715.5915MSANS
+6569GAULLE JEAN CLAUDE 11962 91989041992121249910472.3015MSANS
+6715SEINE KARL 11962 719840219830211131 9657.1215MSANS
+3136GOBELINS JEAN PIERRE 11962 21993091992011249910472.3015MSANS
+5291LAMBERTIANAS MARIE CLAUDE 21962 7199506199406 9126 8457.5615DSANS
+6342MISSECLE DIDIER 11963 41989111988111539912453.6015MSANS
+3447VIAL GEORGES 11963 51985041984111527112374.3415MALLEM
+5045GRAZAILLES LAURE 21963 8199108199008 8656 8146.9715.INFOR
+7974CHAUSSEE PAUL 11963 61985071991121288210743.9215MSANS
+2189PEYBERT DIDIER 11963 81983081988112090215980.4615MSANS
+7262JOZE PATRICK JEAN 11963 91983111982111326611017.4115MSANS
+8222ETANG LIONEL 11963 71985011993031693613497.9015MSANS
+6881OUEST OLIVIER 119631219840419840210791 9502.0815MSANS
+8121MADELEINE JEAN 11964101984071983121288210745.1815CESP
+6828CONCY JEAN MICHEL 11964 91988041994091693613499.2215MALLEM
+3939CHURCHILL BRIGITTE 21964101992021991021330611016.6015MSANS
+5116PEREIRE DIDIER PAUL 11964 21986071992121288210745.9915MSANS
+2174TEISSEIRE ELFRIEDE 21964 4199212199112 8656 8145.5615.ANGL
+0585CRETS LAURENCE 21965 3199306199209 9597 8728.8415MSANS
+7988DOMAINE PASCAL 11965 61984051993071527112372.8715MSANS
+1646PERDRIX MARCELINE 21965 51985101984101288210745.7015MSANS
+7090KERAVEL GILBERT 11965 11988041994091693613498.2215MSANS
+3100BARILLERIE BRUNO 11965121988111987112188616679.7215MALLEM
+8075ESCALE YOKO 21965 61991061990061381911365.3215MSANS
+1009CHARRIERE SYLVAIN 11965101993021993121249910473.1115MSANS
+8542MOLIERE PIERRE 11965 51987111995021450311830.6215MSANS
+8729ROSTAND PIERRE 119661219860419850410791 9503.7015MANGL
+8802DOUMER JEAN LOUIS 11966 619860319930110791 9502.8415MINFOR
+8596ABEBERRY PATRICK 11966 619891119910910407 9270.2415MALLEM
+6485CHALEUTRE GILBERT 119661219870719860710407 9271.9715MTECHN
+9054FAURE FRANCIS 11966 519860719890710791 9503.7915MALLEM
+6168CHENEAUX COLETTE 21966 61994021992011202810240.1915MSANS
+9809PESSOT JEAN JACQUES 11966 11992011991011202810241.3015CALLEM
+8041BIGUE THIERRY ALAIN 11966 619860419850410791 9503.8515CCOMPT
+7730CALIANES STEPHANE JEAN 11967 3199404199304 9894 8921.6315CSANS
+2161TOUCH MICHELE 21967 11989111988111949415052.4015MSANS
+1999LISLE GILLES 11967111990021989021539912451.6115MMICRO
+2038GRANDES ROGER 11968 2199312199212 9597 8728.0115CSANS
+4888BELMONTET ANN 21968 619960419950410668 9426.5315MSANS
+5809ROUSSIER DENIS 11968111990011989011467711987.1615CSANS
+0146ROSESCERNY MIREILLE 21968 5199602199502 8656 8145.5715MANGL
+3432GARCIA PHILIPPE 11968 51990121992041249910471.8915CSANS
+3058SANGRIA CAROLINE 219691119910519900510024 8998.6815MANGL
+7679BLEU CHRISTIANE 2196911199207199107 8656 8146.3815.SANS
+0602VAUGINES GILBERT CHRISTIAN 11969 91991061994011249910471.7615CCOMPT
+5226BOULEE PHILIPPE 11969 819900419890410407 9271.7215CSANS
+5904ORANGERIE MARIE CHRISTINE 21969 1199001198712 8656 8146.0115.ALLEM
+8131NICE CHANTAL 21969 41989031990011450311831.6615CTECHN
+8570SOULAGE MICHEL 11969 41989091994031330611016.4415MVENTE
+5857ALLEE JACQUES 11969 7199304199206 9597 8728.9515MANGL
+6920LOTI YVES 11969 919911219901210024 8999.3115CVENTE
+3599PLANA JEAN PIERRE 11970 61991051990051467711987.6115MALLEM
+7579BERGUEROLLES MARIE CLAIRE 219701219910919900911389 9852.4115MSANS
+0494DESBORDES BERNARD 1197012199101199001 8656 8145.1215.SANS
+9496MONTBAURON ERIC 11970 61991051990051467711986.2215MSANS
+8603DESCENTE FRANCOISE 21971 5199607199507 7803 7680.9915MSANS
+2449TAHITIAA MIE 21971 619940919930810668 9425.5515MSANS
+7786LOUP ISABELLE 21971 61991121993041202810240.8915MANGL
+6100MARNE JOEL 119711219940719940111389 9851.4615MSANS
+8569ASPIRAN ANDRE 11971 61991121994011249910472.8415MSANS
+0626ODET PAULE 21971 2199611199511 7803 7681.0415MSANS
+7006ARGENCE PHILIPPE 1197212199308199208 9597 8728.2215CANGL
+2516MAILHEAUX LAURENT 11972 41991121991061202810241.5215CSANS
+4928COLLEDEBOEUF ANNE MARIE THERESE 21973 51994071993071206910240.6415MANGL
+1718HACHETTE ERIC 11974 7199606199506 7803 7679.4615CALLEM
+5240MUSES JEAN BERNARD 11974 9199507199407 9894 8922.4415CANGL
+3654MAIRIE MARIE 2197412199507199407 7803 7680.6315CSANS
+3017BAUCHAT MICHELE 21975 8199505199405 8359 7952.4615MSANS
+1395BROSSOLETTE MARIANNE 21975 4199609199509 7803 7679.7615CESP
+9154REYNARDE FREDERIC JEAN 11975 8199406199306 7803 7680.3415CANGL
+3041BOYER ERIC 11975 3199406199306 7803 7679.3315CSANS
+8817FILOURIE FRANCOISE RENEE 21976 8199506199406 7803 7679.3615CANGL
+3408BRIE BRUNO 1197611199508199408 7803 7679.4515CSANS
+6446WYLLIE PATRICK 11976 1199507199407 7803 7679.4915CSANS
+7476SOMME BEATRICE 21976 4199606199506 7803 7680.8615CSANS
+8951BARNIER MARTINE 2197612199506199406 7803 7679.4915CTECHN
+5707FOCH DENIS 11977 7199607199507 7803 7679.3615CCOMPT
+9331PEYPIN MICHEL 11977 3199506199406 7803 7681.2015CALLEM
+2044RENAN BERNARD 11977 7199607199507 7803 7679.9915CSANS
+0991GUTENBERG BERNADETTE MARCELLE 21930 6199207199107 9894 8922.1616MANGL
+4650NORD PASCALE 21930 6199001199506 8827 8262.8016.SANS
+7670BOURG ERIC 11935 11967071994011433211713.8816MCOMPT
+1067ITALIE SVETLANA 11935 81973071991011433211713.6116MSANS
+7446BELMONT MARIE JEANNE 21936 4199010198910 8656 8145.0316.ANGL
+4567VICTORET DOMINIQUE 11936 2198812198511 8656 8146.6116.ALLEM
+1382ARIES DOMINIQUE 11936 11970081991041433211713.8816MANGL
+7727TERRASSES BERNARD JACQUES 11936 91957051994123417325096.9816MALLEM
+2304MERCUES JACQUES ANDRE 11936101955061992113327724476.7316MSANS
+1717FEUILLETS EDITH 21936 51971091992021932314935.3116MSANS
+5274VALLEES PASCALE 21937 8198901198511 8656 8145.6216.INFOR
+1751BESSE WILLIAM 11937101959021994065760041273.7516MSANS
+7544TRAIL PATRICK 11938 41971081991101433211714.7916MINFOR
+0247VIARD CHRISTIAN 11938 11962121984073200223583.9016MTECHN
+9383CHALONNIERE MARIA 21938 4199009198909 8656 8147.1916.SANS
+1762EMERAUDE BRIGITTE 21938 11975051995011369311288.0616VESP
+8379MAGNIOUX NADINE CHRISTINE 21938 81966071990071578112723.4716CTECHN
+4635CYTHERE CORINNE CHARLINE 21939 4199201199101 9085 8418.3916MANGL
+2414PILATTE MARIE JEANNE 21939 11962051983032470018540.4716CSANS
+9003MOULIN DIDIER PAUL 11939 51980021995121471512025.3716MINFOR
+6102MARINVILLE ERIC ANDRE 11939 21971031991101433211714.6416VANGL
+9945ELANCOURT AGNES 219401119841019810111131 9658.0916CVENTE
+5615THORELLE FRANCOIS BERNARD 11940 219840119840311131 9658.6416MALLEM
+9231HERBILLON MADELEINE 21941 11968071990071578112722.1216MSANS
+9547REMY CATHERINE 21941 31965121990012273717261.0916MTECHN
+8538DAMPMART THIERRY 11941 41964071994013200223582.7616MSANS
+4432ECOLE CLAUDE 11941121966071995041557012566.5416MALLEM
+7122SORRIERES JACKY 11941111975031992011403411520.6216MSANS
+5855CESARI PHILIPPE 11941 41972081992011433211715.6916MSANS
+4569BELTCAGUY JEAN PIERRE 11941 31974041991011369311288.0016MSANS
+0119BAUMETTES VONICK 21942 7199309199209 8656 8146.7316.SANS
+5799ICARD JACQUELINE 21942 31968071994031834514275.4316VTECHN
+3230NIBELLE PHILIPPE 11942101963011990042273717261.8816MTECHN
+1160PLAY JEAN DANIEL 11942 81975091993041369311287.9716MSANS
+8701ORRIANES DIDIER 11942 81964101989122470018540.0616MTECHN
+9211ANJOU JEAN CLAUDE 11943 81969101991011433211715.4716MSANS
+2069CHURCHILL ANDREA 11943 61970061995042201216756.8916MINFOR
+4457ARBAUD JEAN CLAUDE 11944 61971011992102137316332.3116MSANS
+9480SABLONNIERES CAROLINE 21944 11962121991072273717261.7616MALLEM
+1573HOCHET HELENE 2194410199208199108 8656 8145.3416.ESP
+8504ANDRE FRANCOISE 2194510199301199201 8656 8145.4216.SANS
+2608THIRD ALFRED 11945 91971041990012137316331.6616CSANS
+0698CEDRES BRUNEL 11945 91968061990071433211714.3916MSANS
+8224CLOSERIE MARTINE 21945 61971061991101932314935.2616VTECHN
+6798MONTESPAN JEAN NOEL 11946 21983121993011215710278.5716MTECHN
+4449TRAW ALAIN 11946 21976091993041369311287.8216MSANS
+1458BOIZEAU PHILIPPE 11947 11973071993041518612334.9216MANGL
+8853OUF HENRIETTE 21948 91970011994062201216756.8016MALLEM
+0743GEORGE BERNARD ALAIN 11948111973021991011433211714.9316DALLEM
+2218GEMEAUX GUILLAUME 11949 51970101992072739020404.5916MSANS
+3154MONTAGNE SERGE 11949 31973061992011403411522.1316MSANS
+2871VILLERMONT EVELYNE 21949111970061993102175716601.0116MINFOR
+8174SARDAN PATRICK 11950 61978051990091966515206.0716MANGL
+5180LONE ALAIN 11950 31978021994011369311287.4616MANGL
+7155COSSON DOMINIQUE 11950 41970101991012990722150.5216MANGL
+7616QUAI FRANCOISE 21951 51973101991071578112723.5116MSANS
+1943VAUGIRARD DANIELLE 21951 7198810198709 8656 8145.3916.SANS
+8270LOUPS CHRISTIAN 11951111979031993011471512024.3816MALLEM
+2654PRESSENS ANNE MARIE 21951 31973011976011288210745.5416MINFOR
+8175COUDOURON LUC 11952 21978061995011249910472.6616MSANS
+9368DOLMENS CLAUDE 11953 81977081991071578112723.3816MSANS
+3828ESTIENNE PATRICK XUAN VINH 11953 71984071992041326611016.1116MINFOR
+8961APPART JEAN LUC 11953101973021992102017815516.1216MSANS
+9113PERIGUEUX JOELLE 21953 21973101991071578112723.0616MINFOR
+7735FLEZ PIERRE 11953 11978071994011403411521.0216MINFOR
+4018FONTMERLE MICHELINE 21953 8199311199211 8656 8146.9516.SANS
+5157GREZ FRANCIS 11953111974051992082350617725.9216MTECHN
+5311SUFFREN MARIA 21953 41973021990101578112722.9116MANGL
+7764BRON CLAUDE 11953 91979081994011249910472.9216MINFOR
+0120POSTAL CHRISTIAN 11954 31976071994011369311288.1516MSANS
+5265HUGO JEAN 11954 51975021993092256617146.9116MSANS
+5590SOPHIE SUZEL 219541119960119940110668 9425.3116VSANS
+5401LARREY MONIQUE 21954 31975041995101881314585.7316MSANS
+6562LANTERNE CLAUDE 11955121980061995041471512024.3516MANGL
+1519GOURDON SYLVAIN 11955 51980051995011249910472.9716MVENTE
+3963RUISSATEL MICHEL 11955 51979051994011249910473.2216MSANS
+3870COMMANDERIE BERNARD 119561219850419870910791 9503.3616MANGL
+6506SUPERIEUR JEAN CLAUDE 11956 31979011994011249910472.3016MINFOR
+0863FIGUIERES JEAN PIERRE 119561219890919881210407 9270.6216MSANS
+1566GROS ANDRE 11956 419950119950110363 9232.9816MSANS
+2124CHASSEURS AUDE 21957 219920619930110024 8997.9516MINFOR
+5594VAAGGASSE CLAUDE 11958 71978051992011471512024.3916MALLEM
+7033TITAN ERIC 11958 41978011992021471512025.2516MSANS
+5186CEYRESTE JEAN 11958 91980091993041471512024.7116MSANS
+7174SEPTEMBRE PIERRE 11958121978081995071369311286.1516MSANS
+4207REGARD GINA 21958 1199107199007 8656 8146.5216.SANS
+0723GARDONNE SIMONE 21958 21989081991101249910473.2416MALLEM
+3190MONTAUROUX FRANCINE 21959 61989011991071249910473.4116MINFOR
+7379SUD PHILIPPE 11959 81982051992011527112374.2216MALLEM
+0598FERBER GUILLAUME 11959101981051995082256617146.9116MINFOR
+6321CEZANNE ALAIN 11960 41984041993041326611016.0616MSANS
+6273FONVERT MARC 11961 619901019891010407 9271.5916MSANS
+6867GENEVE ANNE MARIE 21961 31983011982011608112916.2516MSANS
+7828PUISEAUX BERNARD 11961 5199403199303 9597 8728.1416MSANS
+7252CHEM MARC 11962 41986091992041949415052.0616MSANS
+0052TEMPLIERS CATHERINE 21962 61982021989041608112916.9716MSANS
+4660HAROUSTA PHILIPPE 119621119900119890110024 8998.9516MVENTE
+3619MURAT PIERRE 1196212199406199306 9126 8455.9716MINFOR
+1366REINE GEORGES 11962 5199606199506 9126 8456.6416LSANS
+6020YVART ELISABETH 21962121994051992011202810241.1616MANGL
+9740SEJOUR DANIEL 11962 81982121992041326611017.0516MSANS
+3790BEAURONNE PAUL 11963 419840619830610791 9502.6816DSANS
+0831VICTOR MICHELE 21963 11985121984121288210744.4116MSANS
+5171NEIGE DANY 21963 21985051984051527112372.9916CSANS
+4834CROIX CLAUDINE 2196310199101198807 8656 8145.0616.SANS
+9575HILL DOMINIQUE 21963 319951219941211389 9852.5416MSANS
+1494FECAMP DANIEL 11963 31991041990041381911366.6116MSANS
+2944MONCET THIERRY 11963 91991081992051249910471.9416MCOMPT
+8018DRAGON XAVIER 119631019830519820510791 9503.5216CCOMPT
+7258CASTELLAS LAURENT MARC 11964 31984091992121288210745.9316MSANS
+3956CORDIERS PHILIP 11964 219861019851010791 9503.0416MSANS
+1434TRESPOEY HENRI NICOLA 11964101987041991091450311831.5216MTECHN
+7725MASSANE LAURENT ALEXANDRE 11964 819850919840910791 9503.9916MALLEM
+5509FLAMANTS OLIVIER 11964 219890519880510407 9271.8216CSANS
+9732VENT THIERRY 11964 91987071988121288210744.9116MALLEM
+8053CAUSSINIERO ANNY 21965 8199610199510 7803 7680.3216MANGL
+8933CHOPIN VERONIQUE 21965101986061994091288210744.0816MANGL
+4294GRAVIER PHILIPPE ARTHUR 11965 61994041992011202810240.0416MTECHN
+7996LOUPIATS CATHERINE 21965 3199312199212 9597 8728.5816CSANS
+5751DIMANCHES JEAN FRANCOIS ROLAND1196511199404199201 9597 8727.9916CSANS
+3314COPERNIC NATHALIE 11965 91993111992011202810239.8316MALLEM
+8095MALIKA ESMERALDA 21966111994051992011202810241.6116MINFOR
+2514CANGINA MARIE CLAUDE 21966 519951219941210668 9426.3416MANGL
+8137BARBE SOPHIE 21966 61985061984061202810241.6116MSANS
+2086FELIBIEN GILBERT PIERRE 11966 619860919850910407 9270.8916MINFOR
+5743FOUESNAN PHILIPPE 11966101987021991071288210745.4316MANGL
+7755ALGLAIS BERNARD 11966 81989031988031249910473.3616MVENTE
+4183NEUVICQ MARIE CLAUDE 219661119950119930411389 9851.0116MSANS
+5854BINEAU BRUNO DANIEL 11967 51991081990082107316097.0316MSANS
+1015HUSSON MICHEL 11967 31990041989041467711985.8116MINFOR
+8894VEGA YANNICK 11967 1199311199211 9597 8729.4016CSANS
+2165POMONE EVELYNE 21967111989031993071381911366.4716CSANS
+3736LYS DIDIER 11968 919950119950110363 9232.6616CSANS
+9047PUITS VINCENT 11968 21990081989081539912453.7216CSANS
+6745COMMANDANT DOMINIQUE 21968 6199403199303 8656 8146.3216MANGL
+2664JAMES CHANTAL 21968 719940419920111389 9851.5216CSANS
+2387LECLERC BERNARD 11968 819950119950110363 9233.9016MSANS
+8096PAROISSE DOMINIQUE 21969 61991091991021330611016.5116MTECHN
+8708BOISSY BERNADETTE 21969 7199006198708 8656 8145.2116.SANS
+7358CUQUERON MARTINE 21969121994031992011202810240.4616CSANS
+5192BESSOU ROSINE 21969101991111990111839014314.0816MSANS
+1422LAVAUX DOMINIQUE 21969 11992061991061202810240.9816CSANS
+4798IDOLE MARIE CHRISTINE 21969 219930419920411389 9852.1516MSANS
+7101NOGENTAIS CHRISTINE 21969 91989111988111202810240.5916MSANS
+3573MIRBEL ANTONIO 119691019900519890510024 8999.4916CSANS
+8187NORD ROBERT 11969101991101990101467711987.9716MVENTE
+2888CASTELS YVONNE 21970 419960619940110668 9426.8916MSANS
+5902GARRIGUE MICHELE 21970 21994021992011202810241.7616MSANS
+7139BRUANT ISABELLE 21970 919940619920111389 9852.0916MSANS
+8561FREGATE DOMINIQUE 11970 81993041995091267210590.1516LSANS
+8391ESCOURADIERES CHRISTOPHE 11970111991111990111467711986.0816CSANS
+7113FLAVIEN MICHEL 11970 91992121991121467711987.9416MSANS
+1139CHAMPIGNY LIONEL 11970 11995031993041202810241.7416MANGL
+3774FAHNESTOCK MAX 11970 11992101991101467711987.9016MSANS
+3115MAGATIS PIERRE 11971 119960219950211389 9851.0316CSANS
+7606ITALIE JACQUES 11971 4199407199307 7803 7679.4516CVENTE
+2804JOYEUX FRANCIS 11971 21990061992121202810241.0416CSANS
+5659HOULE PATRICK 11971 21993121992121420611637.3316MESP
+9687LANCIERS DANIEL PHILIPPE 11971 8199311199211 9597 8727.9016CANGL
+5871LACOURTADE PHILIPPE 11972 11991121994011249910472.6116CVENTE
+4676LUPINO ATSUKO 21972 6199501199401 8656 8146.1416MTECHN
+2981VIREBELLE FRANCOISE 21972 219940819930810668 9426.5716CSANS
+1979LOMBARDS JEAN DIDIE 11973 6199309199209 9597 8729.0716CSANS
+7372BOIS MONIQUE 21973 1199407199307 7803 7681.1116CSANS
+3844PERIGORD NICOLE 21973 319930919920911389 9850.5616CSANS
+0819THOBIE DANIELLE 21974 1199409199309 8359 7952.6116CALLEM
+8257SEVIN CLAUDINE 21974 4199409199309 8359 7951.8816CSANS
+5832DELAGRANGE MICHELE 21974 3199309199209 9597 8728.8216MSANS
+6357MORTARIEU MARIE JEANNE 21974 8199607199507 9894 8921.8516CSANS
+7800TOURNELLES FLORENCE JACQUELINE 21974 519950119940110668 9426.0016MSANS
+1978CENIS ALAIN 11974 71994091994011202810240.8616CSANS
+4208BEAU OLIVIER 11975 8199510199502 8359 7951.6616CTECHN
+2472TOWERS CORINNE 2197511199410199310 8359 7953.1116CTECHN
+8054QUINTA PASCAL 11975 4199606199506 7803 7680.5016CSANS
+5910JARRY CLAIRE 21976 1199608199508 7803 7680.6616CVENTE
+9513GENISSIEUX HERVE 1197612199607199507 7803 7680.0016CSANS
+6334MUSSET MARIE PIERRE 21977 3199607199507 7803 7681.4716CSANS
+3177VILLEFRANCHE NICOLE 21977 7199607199507 7803 7680.4416CESP
+3707PREVOST CLAUDIA 11934 81963071994011663613343.7817MANGL
+7266OUCHE JEAN LOUIS 119351019930919910110407 9272.2217CSANS
+5677VIRGINIE JEAN PHILIPPE 11938 51963111989071663613342.9817MESP
+0564PEYREBELLE JOELLE 21939 11967011989091932314934.5317MSANS
+6752SILLERY PIERRE 11941 41962061985072273717260.5917MSANS
+7389BILLOUX NATHALIE 21941 3199012198912 8656 8145.3317.SANS
+0440MALASSIS EVELYNE 21942 11993101992011202810240.7217DSANS
+6527CURSON JEAN LOUIS 11946 71974121991011369311287.1017MANGL
+4166EVIAN FRANCOIS 11946 31969101986012470018540.6117MSANS
+6635ROSAY FABRICE EUGENE 11946 71970031989122334017609.4317MANGL
+3999MUSARDIERE CLAIRE 21946 81968111983042470018540.9317VALLEM
+9845YVETTE MARTINE 21947 71972101990071621213032.1717MANGL
+1439FIEF EVELYNE 21947 91974071991071578112723.1817MSANS
+2404ROME BRUNO 11947 91988031995011433211715.0917CSANS
+8776ROME THIERRY 11948 61974011995031911114779.7717MSANS
+5946TERRA MARIE CHRISTINE 21950 91970011991071578112723.6817MSANS
+5232DEBUSSY CATHERINE 21951 81973051991021684813421.7217MTECHN
+0869CARAMEL ELIANE 2195211199207199107 8656 8146.0917.SANS
+1911PLASSAN THIERRY 11954 41980031992031608112916.5917MTECHN
+1670LORETOHOHE MARIE EDITH 21954 11974071993101911114780.0617MANGL
+9187BINGER DOMINIQUE 21955121986091991041249910471.9217DESP
+2614COLLONGUE MARCEL 119561019810319820611389 9852.2917MANGL
+9658BEAUREPAIRE MICHELE 21957 81977071992011471512025.4717MSANS
+1305HOPITAL DOMINIQUE 21957121979071980041684813421.6717CSANS
+2762PELUT PIERRE YVES 11958 119881219871210407 9271.4117MALLEM
+4217ALEXANDRA JEAN MICHEL 11958 71983031993081326611017.7417CTECHN
+8469CRETES PAULETTE 21959 619910719900710024 8999.3117MANGL
+1495RIBAUTE PIERRE LAURENT 11959 119801019820611389 9850.8817MINFOR
+1650BRIAND SYLVIE 21959 91980041992121608112915.9217MSANS
+8360JOUY CLAUDIE MARIE 21959 319940419920111389 9852.7417MSANS
+5653ROLL ROBERT 11959111988061991091450311830.5617MINFOR
+4042TANE PHILIPPE 119591019870719890410791 9503.7017MANGL
+0940YVETTE MICHEL 11959 31978071991032022115555.5017CTECHN
+9875DANREMONT THIERRY 11961 41982091995041433211714.5117MTECHN
+9667CASITA JEAN LUC 11961 81982081995121638413150.9717MSANS
+1371ROME MAURICE 11962 21984051995081710713577.9317MSANS
+6755AUBAIS BRIGITTE 21962121983011993051527112374.0017MTECHN
+2907BRETEUIL FREDERIC VALERY 11962 41993021993081202810241.7217MSANS
+4530MICHAUDES CHRISTINE 21963 11982071981071326611017.7417CSANS
+9935TAZARKA BERNARD 11963 91992111991111330611016.2617MSANS
+1813PLOUGUIE DANIEL 11964 21985081989051288210745.8117MTECHN
+2666DAUX JACQUES ANTOINE 119641219840119830110791 9503.3917CANGL
+4498LOUVEAUCOURT MICHEL 11965 81985061988061288210744.9117MSANS
+6274BRIE PIERRE 11965 21984071983071288210745.4317MANGL
+8475PATHE LOUIS 11966 91987111993091839014314.3417MSANS
+2923LACLOTTE NADINE 2196610199501199304 9597 8728.7217CTECHN
+5049CROZES CAROLE 21966 71987021992011450311832.6817CMANAG
+9666RABINES MICHEL 11966 81986071995121450311832.3617MTECHN
+1503LESTRADE GILLES 11967 119870119860110407 9272.1917MINFOR
+2577JONCQUIERES MARCUS 11967 61987041990091249910471.6517MANGL
+6372VILLARS LYDIE 21967 3199411199311 7803 7680.5917MANGL
+8399HAMEAUX BERNARD 11968 61990071991081249910473.2417MSANS
+0624BLOY MICHELINE 219681219940619930610668 9427.0217MANGL
+0069ARTHUR CLAUDE 11968 71989021993041249910472.5717MTECHN
+9429MATIN FRANCOIS 11969 919890419890410407 9271.0217MANGL
+6364LEONIE SERGE 11969 919900319890310024 8998.4117MSANS
+3990SALANGANE CATHERINE 21970 619950919940110668 9426.0017CALLEM
+9447AYGULF CHANTAL 21970 419950219930411389 9851.1617MSANS
+5807CIMES ODILE 21970 1199404199304 9126 8457.6317MINFOR
+6551FOUCHER CHRISTIAN 11970 81990081989081202810240.2617MSANS
+9323SAUVAGERIE MARCEL 11970 7199512199412 9126 8457.5917MSANS
+0265PEYRIERE YVES 11970 91990111989111381911366.7217CTECHN
+8683BRUSE VERONIQUE MARIE PAUL21971 119960119950111389 9851.9717CCOMPT
+4051CHANTILLY PATRICK 11971 8199602199502 7803 7681.2017CINFOR
+5145ETHORRI BERNARD 11971 9199309199209 9597 8729.2517MSANS
+2756CADDESI THIERRY SERGE 11971 919910719900710024 8998.0117MSANS
+5810HAUT CAROLE 2197212199610199510 8656 8146.9517MESP
+1266GAUTIER VIRGINIE SOPHIE 21972 1199603199503 9894 8923.0217MSANS
+8948CLOUD RICHARD 11972 81991071992041202810239.8317CSANS
+4395DELIBES ERIC 11972 619940219930211389 9852.6917MESP
+9450DAMMARTIN YVES 11972 6199404199304 9597 8729.4517MSANS
+4403MORTIERS BERNARD 11972 219910919900910024 8999.0917MANGL
+1227ROSSAT MICHEL RENE 1197212199407199201 9597 8728.3517CINFOR
+8746VERGER JACQUES 11972 519940219930211389 9851.0117MCOMPT
+9344ECHEZ CLAUDE 11973 9199505199405 9126 8456.2417CSANS
+4420CASA ALAIN JEAN CLAUDE 11973 319930719941111389 9851.8417CSANS
+2752BEAUTE PATRICK 119731219930419920411389 9851.7417MSANS
+0850MULON GERARD 11974 619940919940110668 9425.6717MCOMPT
+2641ESCURIAL JEAN FRANCOIS 11974 3199606199506 7803 7679.5117CTECHN
+2955MALETACHE GENEVIEVE 21974 9199508199408 7803 7680.9817MSANS
+5801COSTES NOEL 11975 8199609199509 9894 8921.3117CSANS
+2146DAUMIER IGINIO 11975 4199310199210 8359 7951.7917.ALLEM
+3984OR GISELE 21975 9199607199507 7803 7679.6717CANGL
+9050GIRAULT LILIANE 21975 5199406199306 8359 7952.9617MSANS
+3948SEIN FREDERIC 1197511199410199310 8359 7953.3217CSANS
+4265MIGNET BERNARD 11976 5199507199407 7803 7679.7817CANGL
+0351RIMBAUD JEAN PAUL 11976 619961219951211389 9852.3617CSANS
+1931BERGERAC DIDIER 11933 3198810199501 8656 8146.3218.SANS
+9698SCHUMAN DOMINIQUE JEAN 11935 71968101995041557012568.3518MANGL
+2020JOLY PIERRE 11935 119870719900710791 9503.6618CSANS
+7058BARGE REMI 11935121955021980013327724475.6718MTECHN
+3747MARIES RENE 11935 51955021988013844228044.8418MSANS
+3141VALLONGUE GUY 11936101976071991011433211715.2418MINFOR
+1098CACTU CATHERINE 21936 41962111991072273717261.7218MINFOR
+9199PLOUHARNEL BERNADETTE 21936 21967011989102273717261.0418DINFOR
+9270ABBE SOPHIE 21937 71968061991041932314934.4118MSANS
+2969BEDOYERE GUY 11937 7199001198412 8656 8146.6418.TECHN
+1219HOCHE JEAN MAURICE 11938 51969051990011433211714.1218MANGL
+1887TOURNON GILLES 11938 51967051990011433211714.1918MANGL
+5417AMBREVILLE FRANCE 21938 51973071990071578112721.7618VSANS
+1954SOPROCOM PATRICK 11938 71973101992011403411522.1318MSANS
+0028ROLE PHILIPPE 11939 81963111994011557012567.1418MSANS
+6857KERPADIRAC EMMANUEL CLAUDE 11939 71965051990011433211714.6418MANGL
+3697PEYRUIS BRIGITTE 21939 81962061991021834514276.0918MSANS
+5636SAVE RENE 11940 21969061991101433211714.1918MSANS
+5290VIALE PHILIPPE 11940 11974071991011369311286.8418CINFOR
+2591COLLE FLORENCE 21940 3199207199107 8656 8146.5218.SANS
+7651GOZLAN XAVIER JEAN 11940 41960031993033200223582.7318MSANS
+8388MONTROUGE LOIK 11940121974071992011403411520.2018MTECHN
+3384LIVRON FRANCOISE 21940 71964071992072273717261.1818VSANS
+7291MARLY MARTINE 2194010199206199106 8656 8145.8318.INFOR
+2644EAU FRANCIS 11941 11968121990011433211713.5818MSANS
+0515WRIGHT PHILIPPE 11941 21973061992041403411520.2118MSANS
+1243SEMAPHORE ANDRE 11941 71969111986072470018540.5618MTECHN
+1791PASTEUR BRIGITTE 21941 61974101992011578112722.8818MTECHN
+1226SAUD JEAN PIERRE 11941 91973081995011621213034.1018MANGL
+3874PASSERINES DOMINIQUE 21941 61966021992042273717260.5318MANGL
+5394QUAY YVES 11941 91973041990101684813421.4518MANGL
+0308BELLIVIER MICHEL 11941 21963091988043417325098.6318MSANS
+8445HEROS SYLVIE 21942121963091991082470018540.5318MSANS
+7703CERF NICOLE 21942 51978071992121471512025.6118MSANS
+3807REBAIS FRANCOISE MICHELE 21942 31972041989011621213032.7018CSANS
+5173TARCO MARC 11943 51973051993011369311286.4718MSANS
+3544RESTANQUES SERGE 11943111971121990011433211713.8018MVENTE
+7289FRAMBOISIERE DANIELE 21943 51969071991011578112721.6718CALLEM
+2761WYLLIE ANNIE 21944 41963121985051932314935.2918MSANS
+7593AMPHORE BERNARD 11944 71964101989062922321684.7218MANGL
+7373PAMPERIGOUSTE MICHEL 11944 21972101992011403411521.3418MSANS
+1159PATIOS ELISABETH 21944 91970101991101932314935.2618MSANS
+6999BEL ALAIN 11945121969101990011433211714.7918MSANS
+7962VIGIE MICHEL 11945 91971071994122295317300.3718MTECHN
+3123GAULLE ERIC NOEL 11945 51973121991082470018540.6918MMARKT
+9943DOUMER PATRICK 11945 51968041990071433211715.1118MSANS
+3765IMPERATORS GHALIA 21945 61965021993102137316332.1118MSANS
+6688COULOUNIEIX HERVE JEAN MARIE 11945 61971031991062273717260.7718MSANS
+0965JUDITH BERNARD 11945 51966051987122470018542.2418MINFOR
+3763MERMOLLOD DANIEL 11945 21972071992011433211713.5318MSANS
+0042POUTGE PHILIPPE 11945 81968081990101433211715.7218MSANS
+2250JACQUIN JOEL 11946 41971071991102022115556.3818MSANS
+7112REDOURTIERE VERONIQUE 21947 3199303199203 7677 7603.7718.ANGL
+5217SARRAZINIERE MICHELE 21947 51968111991102137316330.6718MINFOR
+1448MAUPASSANT PHILIPPE 11947111968121988102273717260.8118MSANS
+7506CLAP JEAN JACQUES 11947101968061986042470018540.8918MSANS
+5237JAS MICHEL 11948 21975101993121369311287.1918MSANS
+0341MAKILA NICOLE 21948 2198901198510 8656 8145.1118.ESP
+3475REMISE MARIE CLAUDE 21948 41968041989042273717260.3218CSANS
+3575DONAT JEAN MARC 11948 51972121991011621213033.6418MINFOR
+0433CAROUBIER PHILIPPE GABRIEL 11948 5198801198306 8656 8146.1618.ANGL
+9184PINEUILH CATHERINE JEANNE 21948 11968091967091493112142.5618MSANS
+1377KERCOLIN ALAIN 11948 91972061992112137316331.4718MSANS
+7248MERCIER FRANCOIS 21948 71970071993121834514275.1418MSANS
+9014FELOUQUE JEAN LOUP 11948101972121994011621213032.9718MCOMPT
+1756FREMICOURT DOMINIQUE 11949 81969121989111932314933.9918MANGL
+7383BLAISON FRANCOISE MARIE 21949111970071994031834514275.1618MALLEM
+6290TILLEULS BERNARD 11949 61971041990123417325098.1518MINFOR
+2226VIVO DOMINIQUE 21949 41971101995041834514275.6118MTECHN
+8441SARGIS CLAUDE 21949 11973061993082175716601.4318MINFOR
+1520PIC JEAN JACQUES 11949 11975041992041403411521.8618MSANS
+5696DRUGEON PATRICE 11949 81980071995041249910473.1818MANGL
+1048MORIZET ANNE 21950 11970121994051834514274.8318MSANS
+4058FRANCE ALAIN 11950101974091991071578112722.6118MMANAG
+0512MARBO JOSIANE 21950 71972011992102017815517.3818MANGL
+2797GUY LIONEL GILLES 11951 51976051993071894014662.5918MSANS
+9267SAUSSIERE MARC 11951111972041992102017815516.2418MSANS
+6865EMMANUELLA ALAIN 11951 51982041993011369311287.7418MSANS
+0116GALICIE FRANCOISE GEORGETTE 21951 41971101992011471512024.0618MANGL
+6144ROSERAIE DOMINIQUE THIERRY 11951 91972081992112739020404.7018MANGL
+9941JULIETTE CHRISTIANE 21952 71976091992011578112721.6518MANGL
+1879CLAIRVAUX JEAN SYLVAIN 11952 31976041993011369311287.4318MALLEM
+9577PECHEURS DOMINIQUE 11953 31978031995011578112723.3318MSANS
+4352BAR REGIS 11953 71979061994011249910473.4518CSANS
+2194EVIAN DANIEL 11953 81977061992011608112915.1218MANGL
+0566PARADISIER DIDIER 11953 11983111993011215710278.3018MANGL
+8627CHARRIERES PHILIPPE 11954 61974111982102470018540.8018MSANS
+5729BRECHET PHILIPPE EMMANUEL 11954 41974081991012137316331.2818MINFOR
+3643RONSARD PATRICIA 21954121976011982052256617146.4518MSANS
+1486AGEN CAROLE 21954 91977071984112256617147.1818MANGL
+4779CALIFORNIE PATRICE 11955 31975051991101578112723.3318MSANS
+0975BIEVRES ALAIN 11955 61978101993011471512024.4718MSANS
+8614DUSENBACH ALAIN 11956 61979031992011471512024.3918MSANS
+4428SAUZON ISABELLE 21956111977051992011471512024.2618MSANS
+1720EOLE PATRICK 11956 41987111986112090215980.5518MALLEM
+8905JANVIER MARLENE 21957 11979071993041471512024.3018MSANS
+7774GAL PHILIPPE 11957 81977061982041834514275.7218MESP
+3204SAGINAW EDITH 21957 319790719780711302 9774.2018MANGL
+5409LEUN CLAUDE 119581019800719821011389 9852.4518MANGL
+4971CHANTEGRELET FRANCOIS NICOLAS 11958101978061987112256617146.9518CSANS
+5691ALTIPORT ANNE 21958121979101993111471512024.9618DTECHN
+1005FLEURS PIERRE 11958 81981041985041684813421.4218MSANS
+6295WINTER JACQUES 11959 81982081988052090215980.7318MINFOR
+3634CELY JOELLE 21959 31979101995021433211713.6518MSANS
+8425SOPITENIA BERNARD 11959 919790619831111389 9851.2118MANGL
+2714AGNEAUX VINCENT 11959 21980091979091684813422.7218MSANS
+3908SAN CATHERINE 21959111980031989112090215980.7318MSANS
+9522ALLERAY EUSEBE 11959 81982041989052090215979.8918MANGL
+4818CARNOT GENEVIEVE 21960 61981031995021433211714.8218MSANS
+2809VAUCRISES FLORENCE 21960 1199205199105 8656 8146.4618.SANS
+6703LAFONT CAROLE 21960 31993081992011249910472.3918MSANS
+4781RUSSIE FRANCK 11960 1199006198906 8656 8146.2818.ESP
+0025OYANA AYALA 21960 31981011980011202810241.2518MANGL
+4606GARNET CHRISTOPHE HUBERT 11960 41983011993041326611017.3218MSANS
+3285CASTAGNIERS BRIGITTE 21960 61980061986101684813422.5318SSANS
+9855HEBERT GABRIEL 11960 11988091992041249910472.9618MALLEM
+7538RUSTICANA JACQUELINE 21960111981011980011326611017.7018MANGL
+7245HAVRE ALESSANDRA 11961121985031984031608112917.1018MSANS
+2845ROSNE ANNE NOELLE 21961 6199507199407 7803 7681.3818MESP
+4993NOIR GUY 11961 81982031995101638413149.8318MINFOR
+0352NOTAIRES PHILIPPE SERGE 11961121986111993072397418036.8018MSANS
+6114LAMPAUL ROSELYNE 21961 61994021992011202810240.8618CSANS
+8947GUE JEAN PIERRE 11961 41981071995071433211713.7018MSANS
+6115NICOLAS DOMINIQUE 21961121983011982011608112916.0218MSANS
+0276ESQUIERE YVES 11961 81988081992061450311832.1518MSANS
+5271BARGEMON SERGE MAURICE 11962 51992111991011249910471.6518MSANS
+3485SOURCES MARTINE 21962 1199201199101 7803 7680.9818.SANS
+1542BOCAGE MARYVONNE 21962 5199203199103 9597 8728.5818CSANS
+8004CARDINAL MARIE JOSEE 21962 21983051982051527112373.2318MSANS
+7577VIE KAVERIO 11962121982061987111608112917.1318MSANS
+1474RABIAC CLAUDINE 21962 81984061983061249910472.0718MSANS
+7742CAZILHAC DOMINIQUE 21962 51983011982011608112915.6518MALLEM
+1316BRIAND FRANCOISE 21963 21983041982041608112915.7518MCOMPT
+5967MASCREE YVES 11963 91983071988011608112915.3918MSANS
+8262PENSEES FRANCOIS 11963 71985081991091288210745.3018CALLEM
+9624JOFFRE FRANCOISE 21964 71988041987041202810240.7718DALLEM
+1583MONTGOLFIER BERNARD 11964 11984071993041288210745.2718MSANS
+8539GILBERTIN MOISE 11964 11985041986081288210745.4318MSANS
+9414YVELINES JEAN PIERRE 11964111985071984071288210745.6618CANGL
+2500PROVOST DANUTA HALINA 21964 51985051984051381911365.9518MESP
+4860AUBEPINES THIERRY 11964 31993081993082401918075.8118MANGL
+1591CALADE ALAIN 11964 21984071990091288210745.7518MSANS
+8611DUGUESCLIN MARIE NOELLE 219651219880619870610876 9503.9018MANGL
+1890DUC PATRICK 11965 21985091989111527112373.2218MANGL
+8780ANTANANARIVO JEAN PIERRE 11965 219860619931210791 9502.9918MINFOR
+9489CALMETTE JOEL 11965 31991121992122401918075.5718MANGL
+0389VICTORET YOLANDE 21965 1198810198707 8656 8146.2818.SANS
+8865VERDIE CHRISTINE 21965 51990111989111949415051.6818MSANS
+3552LOGEM ISABELLE 21965 3199408199308 8359 7951.8018MSANS
+2210MANCHOULAS PHILIPPE 11965 31994021993021420611638.9518MTECHN
+6427LAUTIN JEAN DANIEL 11966 719890619880610791 9502.0518MINFOR
+9413KERAVILOU HUBERT 11966 51989061993111249910472.6918MANGL
+0446CHENEUSE JACQUES 11967 419931119950910363 9232.5318MVENTE
+8752ALPILLES PATRICE 11967 6199105199005 8656 8146.2318.ALLEM
+8347AICARD BERNARD 11968 219880619880110407 9271.1118MANGL
+2090ALICE JEAN MICHEL 11968 61993041992041202810240.9118MSANS
+8583EPINETTES DENIS 11968 31988041994101249910472.6618CINFOR
+2122SEE PHILIPPE 11968 9199205199105 9597 8728.6418CANGL
+2399ROQUEPAVA PAUL 11968 81988071993111249910472.9118MSANS
+8130FRATERNITE JEAN FRANCOIS 11969 91992061994011288210745.7218MSANS
+7498CARROUGES JEAN MARC 11969 31989041993121249910473.0618MANGL
+3505CHEVREUL JEAN JACQUES 11969 61990091989091467711987.2218MSANS
+2473MAJOR THIERRY 11969 3199406199306 7677 7604.8418CSANS
+7707AYGOSI CORINNE 2197010199502199402 8359 7953.4218MSANS
+6442BERGER NATHALIE 21970 5199607199507 7803 7679.4518MANGL
+5162CHANDELIERS JOEL 11970 7199501199301 9085 8419.1118CSANS
+1417BILLETS WADY 11971 119931019921011389 9851.2118MANGL
+1522PARAME COLETTE 21971 3199606199506 8359 7952.5518MSANS
+2322BARRE JACQUELINE 21971 91991011990011202810239.7818MANGL
+8136TASSIGNY JEAN PAUL 11972 31992061991061249910472.4618MSANS
+2240LOUP VERONIQUE 21973 21994031993031267210589.1018MSANS
+2489VELLEDER MARC 11974 7199506199406 7803 7679.7818CTECHN
+8166GUERRIAS CLAUDE 1197710199610199510 7803 7680.5718CSANS
+6073ALLENDE DOMINIQUE 11977 1199606199506 7803 7680.9018CSANS
+6606FROIDEVAUX FRANCOIS JEAN 11934111958051993013844228045.6538MESP
+5822DITTE NAGUIBA 11937 61966091991072273717260.5038MSANS
+1148KERVENNEC JEAN JACQUES 11938 31971051991011663613342.7438MANGL
+8491ROCHEBRUN MAURICE 11939 51967011992021932314935.2938MCOMPT
+5749GILBERT JACQUES 11939 61968031992102334017610.6938MTECHN
+3667PERGOLETTE JACQUES 11939 31963051988011663613344.6938CSANS
+6409PLACE JOSYANE 21940 71984011991091288210745.1338MSANS
+2498GASNIER CLAUDE 11940 119810719890511131 9657.9738MSANS
+2100BRASSIOUX BRUNO 11942 41973061991011621213032.2438MSANS
+1118HOUMEAU ANDRE 11943 51962111991072273717260.3538MSANS
+4519CAGOU JEAN CLAUDE 11943 41972071991011663613343.2038MSANS
+6013REMO JEAN MARC 11943 91966121990011663613343.0138MANGL
+8050YAN BERNARD 11944111963121992042273717261.8138MSANS
+3614EGUILLON SERGE 11944 31978041992011471512025.5638MSANS
+5430CHARPENTIER JEAN PIERRE 11944 91966061994072137316332.5038MSANS
+8523PARIS PHILIPPE LOUIS 11945101969071992062334017610.6538MANGL
+2111TOUL ROLAND 11947 51972021992012017815518.0638MINFOR
+0683GEORGES RICHARD EMILE 11948 91976091994012000715439.1738MANGL
+7517GRAND PHILIPPE 11950111971041995072470018540.9738MSANS
+4909AIX JACQUES 11950 11970041993121834514276.0138DCOMPT
+3400NOUAN PIERRE 11951 41971041992122739020403.8938MSANS
+7319BERGERONNERIE MICHEL 11951 21979031993011578112723.3338CSANS
+2615PATURES DANIEL 11952 91972081993122547019085.9538MALLEM
+1673MIRAMAR PHILIPPE 11952121973071983112470018541.3438MSANS
+4747LOLIVE FREDERIC 11953 81978111993011608112915.9738MSANS
+9613BORD JEAN MARC 11953 41978051995041249910473.2038MSANS
+9161CHANTE PATRICK 1195311199603199505 8656 8147.0938MTECHN
+1511COLBERT PIERRE MARIE HENRI 11953121973031993042017815517.9138MSANS
+0700BRAGELOGNE ALAIN 11954 91974021989052256617146.1638CSANS
+4590BERRY CHRISTINE 21954 5199302199201 9085 8419.4938DMANAG
+7017LASPLANES DOMINIQUE 11955 41984061993041326611016.6238MCOMPT
+4869ROUMANILLE CLAUDE 1195512199306199206 9126 8457.4938CTECHN
+2931PYRAMIDE ANDRE 11956 61979031992011471512026.0638MSANS
+0636COTEAUX MICHEL 11956 31976011992011608112915.2138MANGL
+9829EUROPE JACK 11957 11980061985011684813422.6838DSANS
+7001FORET MARC 11957 41977011994011249910471.5638MTECHN
+7136OCEAN HENRI 11957121977061992011471512024.5338CSANS
+3955VIGNES PATRICK 11959 51981011993031608112916.1438MANGL
+9568ARNOCHE GERARD 11959 71981061993121471512025.8338CTECHN
+4979AIMEE PASCAL 11959 519860219880110791 9503.5238MSANS
+2891SCHLOSSER BRUNO 11960 51982111995071433211713.6738MSANS
+8194CABIRAUX JEAN LUC 11960 219930119910110407 9272.1338SANGL
+2865LEIGNE ANNE MARIE 21960 71980091993051527112374.8538MSANS
+4343GERMIGNY YVES 11961121983041982041326611017.7438MSANS
+2039ROUTE JEAN CHARLES 11961 21982031981031326611017.8838MANGL
+8887CUTTE HUBERT 119611019820619810611131 9657.5738CANGL
+5692LAC FREDERIC 11961121981051994011433211715.2438MTECHN
+9404COMBES HUBERT 11961 9199308199208 9597 8729.7438MCOMPT
+7675CHEVRIER JEAN HERVE 11962 41984021991071527112374.2538MINFOR
+8147PROMENADE CHRISTIAN 11962 3199405199201 9597 8729.7238MANGL
+8550RASCAS JEAN 11963 119850219900610791 9503.9738MSANS
+8387JEROME PIERRE 11963111985041992011949415051.6838MALLEM
+7757FAVIERE PATRICK ANDRE 11963 51984081991021527112373.3138MSANS
+2903GLADIATEURS MICHEL 11963 31984121983121326611016.0338MSANS
+5686BEDFORDALE THIERRY 11963 819831219821210791 9502.0438CINFOR
+5983TANNERON JEAN FRANCOIS 11964 61987111995042256617147.2238MTECHN
+5469CIGALIERE JEAN FRANCOIS LOUIS 11964 31988081988021450311832.6838MSANS
+2425BOUVREUILS MICHEL 11964 11988011995121381911366.9938MMANAG
+7826EAU ARNAUD 11964 81987021986021249910473.4938LSANS
+4783TESSIER MICHEL 11964 61990011991011249910472.1238MALLEM
+4053ARPENTS PHILIPPE 11965 219901019891010407 9271.1138CESP
+5354COMBELONGE PHILIPPE 11965 41985041984041288210745.9338CESP
+0915TUILLIERE JEAN LOUIS 11965 51989031991011249910471.5838MCOMPT
+3732MALABRY FREDERIC 11965 219940119910110407 9271.7938CSANS
+8032VIRO PASCAL 11966 319921119911110024 8998.3738MSANS
+0237TORRICELLI ERIC 11966 61988101986051288210744.7638.SANS
+9815LAPLACE PHILIPPE 11966 5198912198812 8656 8146.1438.SANS
+0253TALLOIRES ERIC 11966 61989121993071381911366.0338CSANS
+7893BOURBOTTES JOEL 11966 8199404199201 9597 8729.0338MANGL
+2674SET OLIVIER 11966 619891119881110407 9270.5638MSANS
+8321LONGS FREDERIC 11966 219950319940110668 9427.0238CSANS
+6369DOURBIES PATRICE 11966 319920319910310024 8999.3438CSANS
+1563TURBINE JACQUES 11966 91987031994091693613498.1438MTECHN
+6038COLLINE RICHARD 11966 5198912198812 8656 8147.2238.ANGL
+8370BONSON THIERRY 11967 5199011198911 8656 8145.3838.MANAG
+1538FAUVETTES HUGUES 11967 6199609199509 7803 7681.3138MANGL
+5092DJINNS MICHEL 11967 1199111199011 9597 8728.9938CSANS
+3468GARDES FARID 11967 119880319870310407 9270.4238MSANS
+7303DENY JEAN CLAUDE 11967 51993021992021202810240.7738CESP
+1319PLOUGONVELIN JEAN FRANCOIS 11967 919880919870910407 9270.1638MTECHN
+7182BEAUMARCHAIS ALAIN 11967 11990111993071381911365.9838MSANS
+5432ROMAIN JEAN PIERRE 11968 519940519920110024 8998.1738CANGL
+8756PEGUIERE PHILIPPE 11968 919940319930310668 9426.6338MTECHN
+0590MONTROSIER DIDIER 11968 419940419920111389 9851.6438MSANS
+3773FIORI MICHEL 11968 61990051990051381911364.9938MSANS
+9060CHAMBORD AGOSTINI 11968 9199406199306 7803 7679.9138CSANS
+7693DIZIER THIERRY 11968 319920519911111389 9851.7338CSANS
+7838CECILE PATRICK 11968101988071991011249910473.6738CSANS
+6964GOYRANS GABRIELLE 21969 91990031992011202810239.9638MSANS
+0784COSQUER COLIN 11969 719940619930611389 9851.9638MINFOR
+8622COURTAIS JOSE 11970 619960719950710668 9426.6238MSANS
+2794BERGERET CHRISTOPHE 11970 61992051993041202810240.5938MINFOR
+4688RABELAIS FREDERIC 119701219940619930411389 9850.7138MANGL
+9681POUSSEAUX RICHARD 11970 61991041990041202810241.4538MINFOR
+0530BRANCOLAR PIERRE 11970 619900619890610024 8998.3538CANGL
+9957LENTILLY LAHOUARI 11971 7199607199507 7803 7680.6638CSANS
+7210CHAMPERRET ANDRE JEAN 1197111199406199201 9597 8728.6838CTECHN
+7549BRAMO JEAN 11971 2199612199401 9597 8727.9638CSANS
+1719TELEMLY JEAN 1197110199612199512 9894 8922.8038CINFOR
+7574MOUQUET PIERRE 11971 3199304199204 9597 8728.7738CANGL
+1471ETOILE PHILIPPE 11971 3199501199304 9597 8729.9038MANGL
+1536MAISTRE LOUIS 11971 51990121989121202810241.5238MINFOR
+7328BLAMONT BRUNO 11971 31991021990021202810240.4038CSANS
+4093TOLOSANE JEAN 11971 51991071990071202810240.7638CANGL
+8648RAPHAEL GERARD 1197210199408199201 9597 8728.4938CMICRO
+2769GOULET ERIC 11972 119950419940111389 9852.5938MSANS
+9108CRETEIL MICHEL 11972 4199606199506 8656 8145.6638CSANS
+5284POMPONNE ALEXIS 119721219920519911211389 9851.7938CTECHN
+8876LAIGNEAU ALAIN 11972 3199202199102 8656 8146.4138.ANGL
+6070REVOULUN CONOR 11972 2199608199508 7803 7681.4038CMARKT
+8083CLEYRAC JACQUES 11972 5199403199201 9597 8727.9138CANGL
+8314PARIS GERARD 119731119940819930810668 9426.0338MANGL
+4108SEGURET GUY 11973 9199207199107 9597 8728.4638CSANS
+4139PRESSENSE PATRICK 11973 9199608199508 8656 8145.6938CALLEM
+2465CHATEAUDOUBLE DIDIER 119731019950219940210668 9426.4838CSANS
+5213MARTINIERE GERARD 11973 8199608199508 7803 7679.2838CSANS
+0769POUR PHILIPPE 11973 719940919930910668 9425.8538CSANS
+2580BESON MARC 11973 7199307199207 9126 8457.0238CALLEM
+4770LIEUTENANT JEAN OLIVIER 11973 8199607199507 8656 8145.6638CINFOR
+1963SENGA CLAUDE 11973 6199412199201 9126 8457.2238MCOMPT
+2932SOULT CAMILLE 11974 5199604199504 8656 8145.8338CESP
+5177CLOS GILLES 11974 4199506199406 9126 8456.6138CALLEM
+0619MEUR BRUNO 11974 2199607199507 9894 8921.6938CANGL
+3202RUBEU DOMINIQUE 1197411199610199511 9894 8922.0838CINFOR
+1263PONTILLOU DIDIER ERNEST 11974 1199309199209 9597 8728.1738CSANS
+3833NEUVE JACQUES 11974 2199508199408 9894 8921.6438CANGL
+6528NIMES ROBERT 11974 1199410199310 9597 8729.3638MSANS
+9312CHAT ANDRE 11974 7199307199207 9597 8729.6338CANGL
+0437SAUVAGEONNE RICHARD 11974 4199301199201 9597 8728.8238MSANS
+5387PLEHEDEL JEAN RENAUD 11974 8199309199209 9597 8728.9938CVENTE
+5835MAROLLES DENIS 11974 7199609199509 7803 7679.9438CSANS
+5277BURON STEPHANE 11975 4199410199310 9126 8457.5938CALLEM
+0147URSULES PATRICK 1197510199512199412 8656 8147.1738CSANS
+6637DEVANT PATRICK 11975 7199610199510 8656 8146.2938CSANS
+7462BARBICAJA STEPHANE 11975 9199609199509 7803 7679.3138CSANS
+9096HELENA PHILIPPE 11975 81994111994011202810241.8538CINFOR
+7777ENGOULEVANT JEAN CLAUDE 1197511199612199512 9894 8922.0838CSANS
+7823BRENNUS JEAN PIERRE 11975 2199606199506 7803 7681.2038CINFOR
+7353LOOVAS FREDERIC 11976 6199612199512 9894 8922.8938CANGL
+5381BRIAND JACQUES 11976 1199507199506 7803 7681.4038CSANS
+8880LECLERC FRANCOIS 11977 2199506199406 7803 7679.8738CMICRO
+1576MONTMELIAN CLAUDE 11977 9199607199507 7803 7679.3138CANGL
+6203GUECH RENE 11977 1199507199407 7803 7680.2638CCOMPT
+2768MERIBEL RICHARD 11977 7199607199507 7803 7681.2938CSANS
+7366FERNANDEZ OLIVIER 1197712199607199507 7803 7681.0238CANGL
+7525LONJUEUIL SYLVIE 2193011198901199511 8656 8145.9141.INFOR
+8410CAIXA JEAN PIERRE 11935 31956051979013327724476.3741MSANS
+4687FURSTENBERG NADIA 21936 31972041994101881314585.6641MSANS
+9030AMIRAL JEAN RENE 11936 61961081993011663613343.8441MSANS
+0747SURREY TOMOE 2193611199201199101 7803 7680.6241.INFOR
+6230LIGURES JEAN PAUL 11937 81962021994042666519900.8141MALLEM
+5598FONTENELLES CLAUDIE 21937 31962031995102666519899.2941MANGL
+7964AUMONT JOSE 11938 31968121991011433211715.7241CSANS
+1865HUBERT CHRISTIAN 11938121962111982112470018542.1041MSANS
+0974ROMAINE JEAN MARIE 11939 91962111989071663613342.9741MALLEM
+8959BILLEHOU DENIS 11940 41962011993011663613343.2041MSANS
+3134RESISTANCE CHRISTIAN 11941 61970051987012022115555.5741MANGL
+2829MARCIGNY ANNETTE 21941 8198901198711 8656 8146.6141.SANS
+9621AIR MONIQUE 21941111968071990071578112723.4141MTECHN
+2532MIMOSE JEAN 11941 31979031994011369311287.2541CANGL
+6822BERLIOZ MICHELE 21942 81965021992051932314934.6841MSANS
+8381REGRATTIER FRANCOISE 21942 41963101991072273717261.0341MSANS
+1877GASTON APOLLINA 11943 41972061995011663613343.5241MANGL
+9764NORTH CATHERINE 21943101992051995121330611017.8841MVENTE
+0051FABRON GILLES 11943111972121992041403411522.2441MSANS
+2826DESTEY RICHARD 11943101965121990011433211713.6541MSANS
+4901GANDHI FRANCOISE 21943 31969031991011932314935.1641MSANS
+2788ENSOLEILLADE CHRISTIAN 11943 81966011991043844228044.1541MINFOR
+2725SAROIS CORINNE 21943 71972081994051804514081.9541MANGL
+4033GRIVES PHILIPPE 11944 31966101990011433211714.0341MSANS
+0306LUTECE CATHERINE 21944 91977061992011471512026.2241VSANS
+6413TURALURE LOUIS 11945 11967121990041433211715.1541MALLEM
+6065BUARD JEAN JACQUES 11945111969061991011433211714.6441MTECHN
+0713CLAUVEL MARIE CHRISTINE 21945121993101992011202810241.7441MSANS
+2016BENGALIS HENRIETTE 2194510199103199003 7677 7603.0141.SANS
+5741SAUVAGERE ALAIN 11945 91972021989011621213034.1541MSANS
+5896GAILLARD ERIC 11946 31973021995011663613344.4541MANGL
+4390PAROUQUINE MARTINE 21946 81973041995101911114778.4441MESP
+4358MAYE CHRISTOPHE 11946 81970111994051834514276.1841MSANS
+3228PERRET JEAN PIERRE 11946 419800719790711389 9852.6741MESP
+2070COMMERCE MARTINE 21946 11965101995042739020405.2241MMARKT
+2112COT CHANTAL 21947 5199104199004 8656 8147.0141.ANGL
+5488MONTAIGNE ERNEST 11947 81972061989041621213032.6541MTECHN
+4615JAURES PASCAL 11947 319850219840211389 9852.1441MSANS
+3975BENAT MARTINE 21947 9197204197104 8103 7796.4541MTECHN
+5422LAUGIER FREDERIQUE 21948 719850419880110791 9502.2841CANGL
+7269TORTE DENIS 11948 71969081994083916628548.6641MSANS
+9916MARNES JEAN CLAUDE 11948 719931019910710407 9270.0341MCOMPT
+7840GRIGNON CHRISTIAN 11949 51969121989122470018541.4341CVENTE
+5255VERNY ARLETTE 21949 81971061990071621213033.7041MALLEM
+4729SALONINA PATRICK 11949 91974041992011608112916.6141MCOMPT
+5121KISS THIERRY 11949101976041994011369311288.2241MTECHN
+3042FERIC MARIE CLAIRE 21949 51984071994031288210744.1741VTECHN
+3660PLAINES FLORENCE 2194912199303199306 9597 8728.0841MANGL
+6514EPINIERE JANY 21950101972011990102175716601.3041MSANS
+4692HUNTZIGER BERNADETTE 21951 71992101991101202810241.0741MSANS
+6747CHAMPERRET LAURENT 11951 819870719870810407 9270.9741MSANS
+5984JAMET DANIELLE 21952 9199203199103 7677 7603.0441.SANS
+8680SERRALONGUE ANDRE 11952 31977071981102470018541.9541MSANS
+1345LENTILLAC ALAIN 11952 919940119920110407 9272.0141CALLEM
+9942AUSSEL MURIELLE 21953 4199406199306 9597 8729.5841MINFOR
+6512SCIPION CLAIRE 21953121974081992032175716602.2441MSANS
+2634FOCH JOCELYNE 21953 31996011995011249910473.0941MINFOR
+5957CYPRIERE CHARLES 11953 81977091993071369311288.1041MANGL
+7817EMILE SYLVIE 21953121973041994011527112373.0441MSANS
+8678HUNG HUGUES 11954 819810519850611389 9852.6041MSANS
+7968LUBONIS MICHEL 11955 71980041992021471512025.5241MSANS
+3688EBAT ANNE MARIE 21955 31977071995071433211714.0341LSANS
+0140GIRARD CLAUDE 11955 71978111991041578112722.8741MTECHN
+6583EUGENE CHRISTINE 21955 71978051992111608112916.2541MINFOR
+3771CUQUES ELISABETH 21955 11993101992011202810241.6641MSANS
+9329CARRIERO JOSE 11955 719830419820411131 9658.8341MSANS
+6210MACHIEL ROGER 11956 71977031993041471512024.0841MSANS
+7407QUENU PATRICK 11956 71980051995041249910473.3241MALLEM
+7722CARNOT GENEVIEVE 21956 51977051993031608112915.4241MALLEM
+7051PALMIERS EVELYNE 21957 41980041994011471512025.1141MSANS
+6978JACARANDAS ALAIN 11957 11985081993041288210744.7741MANGL
+5352VANTAGE GERALD 11957 91978071992091471512024.5341MTECHN
+9337CHALONS MICHELE 21958 3199609199509 4342 5820.2441MSANS
+6534HERRET VIVIEN 11958 919861119860710791 9502.8041MSANS
+3402RUVEI LINDA 21958 71981051995041433211713.6241MSANS
+5959SERLIANE HERVE 119581119851219841210791 9503.3141CANGL
+2613ROSIERS RICHARD 11958 3199601199507 9894 8921.8141MSANS
+3289ALLEGRES GERARD 11959121981071994011471512025.9141MSANS
+6523ROCCA GUY 119591219850119860411131 9659.1541MMARKT
+8881CHATEAUBRIAND GILLES 11959 51981011993071471512025.3241MSANS
+5781HELSINKI DANIELLE 21959 81981011995011433211715.7441MANGL
+4147SIAGNE CORINNE 21960 61981041980041450311832.2441MSANS
+1606POIL MARIE THERESE 21960 919941119920110024 8999.5241CANGL
+1752ROUGUIERE GUY 11960 319940819951011389 9851.1041CSANS
+5961CHALAIS MICHEL 11960 919930819910110407 9271.4641MSANS
+1601DURUY ANNE MARIE 21960 21989071988071249910473.2741MALLEM
+3631ESPARIAT MIREILLE 21960 4199511199411 9126 8457.4941MESP
+2747TENSE MARCEL 11960 11982021995042256617145.7841MSANS
+6989JACQU YANNICK JEAN LOUIS 119601219831119850111131 9657.0641CSANS
+4612BOURNET LAURENT LOUIS 11960 119900519891110407 9271.2441MSANS
+3236PIOL DOMINIQUE 11960 51984051991011326611017.7741MSANS
+6475VILLETTE PHILIPPE 11961 21984081983081608112915.6641CANGL
+8207MESNIL REGIS 11961 21982101990112090215979.7941MSANS
+0526AGUESSEAU DARA STEFANE 21961 91986041985041288210745.7541MSANS
+3092MONTFLEURY JACQUES 11961 11983041994011433211713.9441MSANS
+7762BUCHERIE ANNIE 21961 91986101985101288210744.6741MSANS
+9535MADONA CHRISTIAN 11961 719931019910110407 9270.8041CSANS
+4953GARE HIROMI 21961121984031994011527112374.4941DSANS
+6431CORNEBARRIEU JEAN PIERRE 11962121985101991021527112373.6241MANGL
+3878ANLHIAC MICHEL 11962 7199602199502 8656 8145.9641MSANS
+6464ESTEREL GASTON 11962 619860319880310791 9503.7641MANGL
+2255MAUVALLAT ANNE 21962 71982081981081326611016.1541VANGL
+8438CHANTACO MARIE NOELLE 2196211199307199207 8656 8147.1541.SANS
+4202FENOUA MARC 11962 3199212199112 9597 8728.5041MTECHN
+3836PAUL PHILIPPE 11962 31982011993041326611016.2141MSANS
+0418CHANET NICOLE 21962121982041981041608112917.1541MSANS
+2005ROCHES MARIE HELENE 21963 31984071983071608112915.2141MINFOR
+7305RASPAIL YANN 11963 719850919840910791 9501.7841MSANS
+4264SCHOENFELD PHILIPPE 11963 619830419841111131 9658.4241MANGL
+4951LAUVE JEAN PIERRE 119631119840319860610791 9502.5941MSANS
+7632VALLOIS FREDERIC 11963101986011990111288210744.6741MSANS
+6021ANTOINE JACQUES 11963 4199406199306 9597 8729.3441LANGL
+1193SAUT YANNICK 11963 51987061992121450311830.7941MSANS
+5840VIRARD JEAN PAUL XAVIER 11963121995011994011206910241.4941MTECHN
+0346CASATORRA BERNARD 11963 419910619900610024 8998.1741MSANS
+0082BAURECH HENRI 11963 41991101990101381911365.5041CSANS
+3466TULIPES ANNEE 21963121984111983111450311830.8541MTECHN
+3635FUSTEL BRIGITTE 21963101983041991091527112373.6441MALLEM
+3776THUITSIGNOL ANDRE 11963 91983071986081326611016.7441MSANS
+7941PINS MICHEL HENRI 11963 81983081993041288210745.4341CSANS
+8903CHARTRES MARIE ODILE 2196412199412199312 8656 8147.2441MANGL
+6455GIANOTTI FRANCOIS 11964 219870119860110791 9501.8341MSANS
+3178PABU PATRICE 1196412199606199506 8656 8145.9641MANGL
+1683TOKI GILBERT 11964101994051993051267210589.2541CTECHN
+1069COUTURES ERWIN 11964 6199504199404 8359 7952.6641CSANS
+7668PALISSY ALAIN 11964 2199310199210 9597 8729.2741MSANS
+4934AIGUES JEAN MARIE 119651019860719860310791 9503.9741MTECHN
+5099LARRALDIA REMY 11965 11987021993091949415052.3541CANGL
+8301BOUCHAREL FABIENNE 21965 81993091992091839014312.4041CSANS
+7214VOUTE CHRISTIAN 11965 419870719870610407 9270.9741CTECHN
+7913MOULINS YVES 119651219890519880510407 9271.8641DCOMPT
+1180REPENTANCE MICHEL 11965101993111992011249910472.8741MALLEM
+1457SAUVETTE GERARD 1196512199301199201 9597 8729.3041MSANS
+4206LONGERE ISABELLE MARIE ALICE21965 8199610199510 8656 8146.1141CANGL
+2030TONELIER MARC 11965 719860419880510791 9502.5541CSANS
+2828DERRIEN JEAN JACQUES 11966 71991011990041202810241.6141MSANS
+5283COULLET OLIVIER 11966 519880619870610407 9272.1341MANGL
+8198COUPANCES CHRISTINE 2196610199403199303 9126 8456.3041CSANS
+4478NOSTRADAMUS GREGOIRE 11966 719860419900210791 9503.9741MSANS
+7215HILAIRE FRANCOIS 119661019860319850310791 9503.9741CSANS
+8745MONTREUIL GUY ROBERT 119661019910419900410024 8999.0741CTECHN
+3647DELESTRAINT JEAN CLAUDE 11966 11989061994011381911366.2741MSANS
+7458LISERB JEAN LUC 11966121991061992031381911366.0941MALLEM
+6758BIGOCHETS FREDERIC 11966 119911119901210407 9270.8341MSANS
+0098JARDINIERS MYRIAM 21966 51986061993071450311830.9441MVENTE
+0882BRUXELLES CAROLINE 219661019940319920111389 9851.8241MALLEM
+5390VEYRE NATHALIE 21966 419941119930411389 9851.9741MALLEM
+4046CHAMBERTE BERNARD 11966 31987091988071949415052.4441CSANS
+4837BONNAMOUR MICHELE 21966 21994031993011723313654.5941MSANS
+0532MORE LOUIS 11967 919870819930410407 9270.2441MTECHN
+4906LELIWA DANIEL 11967 219870619860610407 9271.2041MSANS
+5362MAGNOLIAS JACQUES 11967 6199609199511 7464 7525.6141CSANS
+8779SORQUES GUY 11967 61988041990011249910472.9741MANGL
+1022ETANG GERARD 11967 3199506199304 9597 8729.5841LANGL
+8535DELACROIX JEAN PAUL 11967 619940519930511389 9851.5141MSANS
+4910ARANDELYS HERVE 11967 71990101989101202810241.0941MANGL
+4304EURE ALAIN GEORGES 11967 51991101990101330611017.7941CESP
+0132DJILLALI ALAIN 119671119931119920110407 9271.1941MSANS
+9907BON THIERRY 11967 91987091992111450311830.6141MSANS
+5376REGNAULT DANIEL 11968 51990111991021381911366.7941MALLEM
+0903SUR BRUNO 11968121992051994121330611017.0541CSANS
+7066RENAN FRANCOIS 119681219890619910510024 8999.7441CVENTE
+9334ALL ADELE 21968 9199310199210 9597 8728.9841MSANS
+1100MONTFORT ANNE MARIE 21968 519950219930411389 9851.7441MSANS
+7153PINSON GENEVIEVE 21968 31989111993071381911366.3041MSANS
+4338MABO MICHELE 21968 7199602199502 8656 8147.1541MSANS
+2568GODEAUX GEORGES 11968 61994051993051267210590.4141MSANS
+9355MEUNIER MARIE HELENE 219681219940919930411389 9851.1041MINFOR
+4462CACTUS MICHEL 11968 51993091992091839014312.7641CTECHN
+2155REINOTS DENIS 11968 1199406199201 9597 8729.6641CMARKT
+1708QUEUILLES ALAIN 11968 91991111991111723313654.7041MANGL
+9411MANDRAGORE ALAIN JEAN 11968 81995041994041206910241.6641CSANS
+4930ELVIRE PATRICE 11968 41995121993041202810241.7441CALLEM
+8112PAVOIS LUC 11969121992021991021381911366.7941CSANS
+8181UNIVERSITE OLIVIER 1196912199408199308 9126 8456.6941LVENTE
+6245CAIRE ANNICK 2196911199405199301 9085 8419.1641CSANS
+2166PRETY CORINNE 21969 7199612199512 9894 8922.5341.SANS
+4325COTTAGES YVAN 1196911199407199307 9126 8456.9741MCOMPT
+0898SYLCO ALAIN JACQUES 11969 91989091988091249910472.9641MSANS
+4572DARTHE DIDIER GEORGES 11969 4199503199304 9126 8456.5541CSANS
+9418AUREVILLE PHILIPPE 11969 519900719890710024 8998.2841CSANS
+7504MAROT ALAIN 11969 119910319951110024 8997.8341CANGL
+4672TRUCQUES JACQUES 11969 2199606199506 8656 8145.6041MANGL
+5588POUTILS ALAIN 1197012199405199305 9597 8728.2241MALLEM
+8965SAUCES JEAN PAUL 11970 71992021991021381911366.2541CSANS
+1934PRESSES LUCIEN 11970 11991091990091467711987.9041CSANS
+5600LAROIN LAURENT 11970 91991061990061202810240.6841CTECHN
+8935REINE JEAN PAUL 11970 119920819910810024 8998.7141CSANS
+9304MARCQ MARCEL 11970 719940119930111389 9851.4341CANGL
+5444CHEM LOUISIANE ALAIN 11970 11991021990021202810240.2241MSANS
+7850HUBERT BERTRAND 119701219920619920410024 8998.1441CSANS
+0638FLEUZY PHILIPPE 119701119900419890410024 8998.7141MVENTE
+0729MUETTE CLAUDINE 21970 5199204199104 9597 8729.8541MALLEM
+5723MARCHE LAURENCE YVONNE 21970 919940619930411389 9852.3641CANGL
+1904CHENE JEAN GABRIEL 1197010199501199304 9597 8729.2141MSANS
+1861CREOUNE MARIE THERESE 21970111991101990101330611018.1541MSANS
+5885LONGUET MICHELINE 21970 619950319940110668 9426.5941MSANS
+9271SEINE LIONEL 11971 81993121992121267210589.8241MSANS
+6056CHE FREDERIC 11971 219901019891010024 8999.8541MSANS
+8822BOUSSOLE ERIC 11971 819910419900410024 8998.7641CSANS
+1433COMBE ALAIN 11971 5199308199208 9597 8729.3941CSANS
+5251GRELLOU DOMINIQUE 11971 91992091991091330611018.2441MESP
+6795EMMANUEL MARYSE 219711119920719920811389 9851.3041MANGL
+6104AUDE CATHERINE JOELLE 21971 7199309199209 9126 8456.4741MSANS
+3946PLEUMER CATHERINE 21971 6199301199201 9597 8728.1941MANGL
+9614BOUE FRANCOISE 21971 519921219951011389 9850.7641CSANS
+5568TASSY DANIELLE 2197111199605199505 8656 8146.4141SSANS
+5159CHATEAU GUY 11971 619910419900410024 8998.9541CSANS
+3722VERE ALAIN 119711219930919920911389 9852.6941CVENTE
+8496ORANGINI PATRICK 11971 31994051993051267210590.4241CSANS
+4662CANTE WINNI 21971 9199206199106 9597 8728.7141MSANS
+5032CHATAIGNIER MARIE LOUISE 21971 81991121991091330611017.6441MSANS
+1771PICHOLINES JOELLE 21971101991121990121202810240.9441MALLEM
+8900BOULAINVILLIERS MARTINE 21971 719940619920111389 9852.0041MSANS
+8306GUERRE FRANCOIS 1197112199103199003 8656 8146.2941.ALLEM
+9088LAROQUE MARIE FRANCE 21971121993091992091723313654.6141CALLEM
+6520LIEU JEAN PIERRE 1197112199201199101 9597 8728.2341CESP
+2579HERANDIERE PIERRE 11971 6199606199506 8656 8146.4641CTECHN
+8999JARDINS PIERRE 11971 819910119900110024 8999.3041MSANS
+8513PERRIERES PHILIPPE 1197211199205199412 9597 8728.0941CTECHN
+5836GIRARDON SYLVIANE 21972 119911019901010024 8998.5041MTECHN
+9465DAN FRANCOIS 11972 31994031993031267210590.0541CALLEM
+3970BREGUI GERARD 11972 5199311199210 9597 8729.5241CALLEM
+8911RIVIERE MARTINE 21972 5199310199210 9597 8728.8441CANGL
+8506RIBOT JEAN PIERRE 11972 3199403199303 9597 8728.1341CANGL
+4988CHATILLON DIMITRY 1197210199303199203 9597 8729.2541MANGL
+3678GRES CLAUDE 21972 8199608199508 7803 7680.6241CSANS
+9757CADENELLE PAUL 11972 219951219940110668 9425.8141MINFOR
+8910RENAISSANCE RENAUD 119721019931119921111389 9851.5541CSANS
+4973SAL EMMANUELLE 21972 719920119910111389 9851.3941MANGL
+8099TURENNE MARTINE 21972 8199407199407 9126 8457.5641DANGL
+6683HIRONDELLES MARGUETITE 219721219930919920910668 9425.4541MANGL
+3610BERTRAND DIDIER 11972 8199403199201 9597 8727.9941CSANS
+7709MORINERIE YVES 11972 7199206199409 9597 8729.5241CSANS
+6549BOISSERAIE HERVE 11972 41995041994041206910240.7641CTECHN
+8170MALHEUREUX PATRICK 11972 21991111990111202810241.4041CSANS
+0105CRONSTADT CHRISTOPHE 11972 6199209199109 9597 8729.1741CALLEM
+3953CHARBERYS NICOLE 219721019930819920811389 9850.9841CALLEM
+7071BARBINIERE PHILIPPE 11972 71993031992031267210590.4741CSANS
+4851LETRIMAN CLAUDE 11973 5199602199502 9894 8921.9641CANGL
+0165VAUGRENIER JACQUES 11973 119930919920911389 9852.3841CINFOR
+7080BANCHE ANTOINE 11973 3199306199206 9597 8729.6741CSANS
+1252VINCENNES ANDRE 11973 6199410199310 9126 8457.3641CSANS
+1299SERIGNE LOUIS 11973 4199310199210 9597 8728.2641MSANS
+7759WILDERTON LUC 1197311199410199310 9126 8457.3241CSANS
+8844LORGUE SYLVIE 21973 119930319920311389 9852.3841MSANS
+8332BRANLY BRIGITTE 21973 7199509199409 7803 7680.7241CSANS
+8767DEMOISELLES LUC JACQUES 1197310199301199201 9597 8729.7441CANGL
+6481MANCEY RENE PAU 1197311199411199506 9597 8727.8341CSANS
+9685HERRIOT MARIE CHRISTINE 21973 6199408199308 9126 8456.8241CESP
+5412JANVIER KATY 21973 6199210199110 8359 7953.4241.VENTE
+3049VICOMTAL ANNE FRANCE 21973 2199305199205 9597 8728.1741MANGL
+8606DIDIER MARC 11973 3199308199508 9597 8728.5041MSANS
+5438MOREL JEAN JACQUES 11973 91994031993031267210589.0641MSANS
+1333CERET ISABELLE 21973 3199608199508 9894 8922.4141CALLEM
+8656COUBARD FREDERIC 11973 9199305199205 9597 8729.6641CTECHN
+7939MONOD SYLVAIN 11973 5199301199201 9597 8729.2541CSANS
+2807BOTTERO PATRICK 11973 4199403199303 9597 8728.5341CSANS
+1950VAROISES ELISABETH 21973 619930919920911389 9850.8341MSANS
+0981MARCILLY JEAN RENE 11973111992101994011249910472.1941CTECHN
+7067ROSES PASCALE ISABELLE 21974 3199406199306 8359 7952.7941CSANS
+5670DEVLOO RICHARD 11974 5199512199304 9126 8456.9141MALLEM
+0896CHEVAL CHANTAL 2197411199603199503 7803 7681.1141CVENTE
+7374BAUX MARTINE 21974 6199607199507 7803 7679.6441CALLEM
+2343PANSEROT BRIGITTE 21974 7199406199306 9126 8456.5741CSANS
+8458ARMANDY CLARA 21974 419940719930810668 9427.4041CTECHN
+5573TAKAOA MARTINE 2197412199306199206 8359 7953.1141.ANGL
+1351ROYAN MICHEL 11974 8199606199506 8656 8145.8741CSANS
+2308COAT MARIELLE 21974 21994091993091206910240.3741CANGL
+1077BARBY HERVE 11974 3199609199509 8656 8146.8341MSANS
+8863JUNOT JEAN GILLES 11974 7199610199510 8656 8145.4441CSANS
+3894CIEL JACQUES 11974 119940919930910668 9425.6741CINFOR
+5086FABRE GEORGES 11974 7199407199307 7803 7680.7141CSANS
+3050CROISON RICHARD 11974 9199409199309 9126 8457.3341CSANS
+9319CHAT DIDIER 119751119941219931210668 9426.9341CINFOR
+2636MONTJUSTIN BERNADETTE 21975 8199609199509 7803 7680.8141CSANS
+1156MER JEAN MARIE 1197512199603199503 8656 8145.2141CSANS
+2790RAGEUL SERGE REYNALD 11975 5199603199503 8656 8146.7941CSANS
+4387BRETEUIL ERIC 11975 1199606199506 8656 8146.2941CALLEM
+7191COCHET HUGUES 1197511199609199509 7803 7680.7541CSANS
+7798LARGE DOMINIQUE 21975 8199609199509 7803 7680.8641CSANS
+1442CANDEOU ARMAND 11975 3199603199503 8656 8147.1041CSANS
+6969CHAIGNEAU CHRISTOPHE 1197512199610199510 8656 8146.0241MALLEM
+5660HOULET MARIE ELISE 21975 5199603199503 8656 8147.0441MANGL
+9225TOURNON MARIE ELISABETH 2197612199607199507 7803 7680.8641CSANS
+3312GURGY FRANCK KLEBER 11976 6199606199506 7803 7679.6741CESP
+2078CEDRES JACQUES 1197612199509199409 7803 7681.4941CSANS
+1381ALPHONSE MARIA ISABELLE 21976 3199607199507 7803 7679.6341CSANS
+2376POMPADOUR HELENE 21976 7199606199506 7803 7681.2941CINFOR
+2713RUILLE PATRICK 1197612199507199509 7803 7680.1241CALLEM
+2134FRANCOEUR ELISABETH 21977 6199610199510 7803 7680.3241CINFOR
+7879EGLISE LAURA 21977 8199608199508 7803 7680.6241CCOMPT
+6841GEAUNE GILLES 11978 2199507199407 7803 7680.3041CANGL
+8261LOC PHILIPPE 11935 11955021990084330331421.1942MSANS
+0031DEFEND JEAN YVES 11935 51961081987042273717261.5642MINFOR
+7162PIOT YUMIKO 21935 41969041991051932314933.5575CSANS
+6005DESIDERATO ASSUNTA 21935111962101994012137316332.6342MSANS
+1346NIZON JEAN LOUIS 11936 51971021994011663613343.9542MSANS
+6423CHARTIF ANNIE 21937111962081987082572519241.9042MANGL
+3151EUBAS DIDIER 11938 81963021981053327724476.0342MSANS
+0345KOCK CLAUDINE 21939 91968051995041834514275.4142MSANS
+4204POSTILLON NICOLE 21939 31963121992102137316332.0042MSANS
+1286BERTIN BERNADETTE 21939101967011990011932314935.1342CTECHN
+0588ONZE BERNADETTE 21939 41962101990072273717260.7142MTECHN
+0552ARENE PASCAL JACQUES 11939 21963071988101663613342.9742MSANS
+8335DRAGON BERNARD 11939111963031987012022115555.3642MSANS
+5819CORNEYRETTE MICHEL 11939 41963041983123200223583.0542MSANS
+6812MONADE ANNIE 21940 11961121994072137316330.5642MSANS
+5797PLATEAU ROGER 11941 71964041993113844228045.2942MSANS
+0254FONTAINE DOMINIQUE 11941 41963071992041557012567.5042MANGL
+8521PRELLAVICA JEAN 11941101966061989071663613344.4542MSANS
+1509MOINE JEAN PIERRE 11941 21960031984102273717260.4542MTECHN
+9780LETELLIER JEAN CLAUDE 11941111975071991011369311286.0842SALLEM
+0995HECTOR MICHELINE 21941 91971011991071578112722.3742MALLEM
+0917PUISAGE JANNIE 21941 81962041992042273717260.7242MSANS
+0311LIEVRES ELISABETH 21941111968031990011932314934.1442CTECHN
+2094NOTRE JEROME MARIE AUGUSTE11942101970061993071557012567.0042MSANS
+3489EGALITE SYLVIE 21942 81962041981112470018541.3642DSANS
+2303BUSSY PASCALE 21942101968121991011932314933.8242MMICRO
+2502MEAUX JOSEPHINE 21943 41966061994102137316332.2942MSANS
+9316DANTON ANNE HELENE 21943 91969091991051932314933.6942MSANS
+0398ABREUVOIR JEAN LUC 11943 41962111986012470018540.7542MSANS
+6448FORCET VALERIE SYLVIANE 21944 81964091994072137316331.2842MANGL
+9339TOULON DENIS 11944 91970121994011663613343.1042MSANS
+1107OUZOUER ANNE 21945 11966061992021932314935.2242DTECHN
+1368ALIVOU MICHELE 21945101971101989102273717260.0542CSANS
+9954LANFRIERE JOSIANE 21945 41996011994011202810240.4142DANGL
+1709LUXEMBOURG FABIENNE 21945111972071991101578112721.5342MSANS
+5979DESNOUETTES PASCAL ROBERT 11945 31975061990011684813422.8442MSANS
+3473AULX OLIVIER 11945 61971081991012334017610.4742MSANS
+3451MANEBIT MARIE MADELEINE 21947101972041993121471512025.3442MSANS
+7163GIGNAC MYRIAM 21947 11969081989042334017610.2042MSANS
+8839CORINA DOUGLAS 11947101967041995042137316332.5642MSANS
+7647COCTEAU YVAN 11947 21973051995072547019086.4742CSANS
+9097APARTADO BRIGITTE 21948111974101994011621213033.6142CANGL
+9822VERONIQUES PATRICIA 21949 419791119920111389 9851.0342MSANS
+5016SYLVAFLOR BRIGITTE 21949 51970021994032334017609.4642MSANS
+5933BROUILLARD SYLVIE 21949 31969011991072137316331.1542MSANS
+1734PLAGE LUDWIKA 21950 61971081991012137316331.0742DSANS
+8533LIBERTE PATRICIA 21950121970011995041834514276.1042DANGL
+7399FONTVERT ISABELLE 21950 41970121994051834514275.5542MTECHN
+7055MONTGIVRAY PIERRE 11951 41972081994062547019085.7942MVENTE
+4158CHARTON DANIEL 11951 91980041993041608112915.2642MANGL
+7325NIEL RICHARD 11951 71973081989053417325098.0042MANGL
+7367BRIAND MICHELE 21951 41973061991011578112722.0142MSANS
+0279HEURES PHILIPPE 11951111980041993011527112373.3542MSANS
+5487TOURAINE CLAUDE 21951 41972081991012137316332.7242MALLEM
+1239JALMOUTIERS CLAUDE 11952 31983031991031693613499.3442CINFOR
+4152CHEMST ELISABETH 21952 91972031971031258210512.6242MANGL
+7961ABBE KATIA 21952 51986071991041249910472.5942MANGL
+9227LEPELLETIER MARIE FRANCE 21952 81973051992102017815518.1942CSANS
+2603ESCALE DANIELE 21952101972071990011621213034.0142MANGL
+9280SISLEY BRIGITTE 21953 319740719730711515 9931.0742MESP
+6356ROZIER MONIQUE 21953 51972081990041621213032.3542MSANS
+6175CHENNEVIERES MARIE THERESE 21953111980101991011249910473.1542DSANS
+0679FONT EMMANUEL 11953 81984081993081326611018.0942MESP
+2032VIALA DANIEL 11954 71973061991011578112723.0642MSANS
+7857MASSEL GILLES MARCEL 11954 81976011995072256617145.4842MTECHN
+4251CLINCHAMPS ANITA 21954111977101976101279810668.5342MALLEM
+4945COURS ERIC 11955 71988101992044121428006.7742.SANS
+1834FUBLAINES BRUNO 11956 91976041995042000715438.5042MSANS
+3825KRIMAHER JEAN CLAUDE 11956 91984081992061326611017.8242MSANS
+9642MORILLOT CATHERINE 21956 319950419940411389 9851.7942MINFOR
+7414DUVEEM FRANCOIS 11956 51988041992011168610047.2742MSANS
+8488BOUGAINVILLIERS PATRICIA 21957 31978071977071224010317.3642MSANS
+3611COURSEGOUL DOMINIQUE 11957 21984081993011215710278.5342MSANS
+5494LABROUSTE AINA 21958 81980011993111471512025.4142MTECHN
+9526FLEURY MARIE CLAUDE 21958 319880719890710407 9270.6042MSANS
+2858LABBE FRANCK 11958121980111994051450311831.3442MMANAG
+3479NOISETIERS MONIQUE 21959 31989011991101249910473.6742MCOMPT
+0868ODET CORINNE 21959111989031988031249910471.6542MSANS
+3022TORSE PATRICK 11959 31978121993012022115555.8642MSANS
+3375ORLAMONDE MICHELE 21959121986091993051450311832.4142MTECHN
+5711PASTEUR CHARLOTTE 21959 71980051994011471512024.9242MSANS
+1466PERRIERE MAUD 21959101979081993071471512025.7442CSANS
+9464ROS ANDRE 119601019900419940110024 8998.4042MSANS
+9586TRAVERSIERE ALAIN 11960 11984121993041527112373.3742MINFOR
+4466GATTIERE BRUNO 11960 41982081992081215710280.1042MSANS
+4368GALIGNES GENEVIEVE 21960 61982091988031326611016.2442MSANS
+2690CHERBOURG HENRI 11960 91983111992091326611017.5542MSANS
+2470LEPLESSIS JEAN FRANCOIS 11961 31984041994011578112722.3742MSANS
+7595GLOIRE MARIE LAURE 21961 519940219920111389 9850.6542MTECHN
+1338SCAMARONI PATRICIA 21961111981111980111608112916.9742MANGL
+2208PLAINES MICHELLE 21961 21982021981021326611017.4742MSANS
+2517REMENIER MARTINE 21961 7199409199309 9126 8457.3642CTECHN
+9410BARRELET GENEVIEVE 21961 41983021982021326611016.3542CSANS
+2551POLIENAS MARCEL 11962 419870319860310791 9502.4942CCOMPT
+7760DANG YVES 11962121982011993091949415051.2742MSANS
+2116CEZEROU PHILIPPE 11962 71983051992022529918968.4642CMICRO
+8052PECH CHRISTIAN 11962 31986021993091839014314.4942MSANS
+0931SALSIBURY ANNE MARIE 21962 31982071981071326611018.0142MSANS
+8826SAUCERRE ELODIE 21962 81986041985041288210743.9942CSANS
+0716BEAU IRMGARD 21962 5198607198507 9085 8417.9142MANGL
+1742VERSEAU MICHELINE 21962 71982111981111527112373.3542MALLEM
+8835EXPERT FRANCOISE 21962 11983041982041608112917.0042MALLEM
+4580TIGNES GHISLAINE 21962 619921019920711389 9851.6442MINFOR
+7561SCIEZ MICHEL 11963 71991111990111467711986.1942MANGL
+4437OEILLETS JEAN FRANCOIS 11963 619930219920210024 8999.8142CTECHN
+9877LIEUDIT RICHARD 11963 51984101993071527112374.6742MSANS
+9511KERNEVEL VINCENT 11963 91985051995101450311831.2142MALLEM
+7509TARN ERIC 11963 619890219930110407 9271.8242MANGL
+1089BERE CHRISTOPHE ALEXANDRE11963 51985071992011288210745.8442CSANS
+7426MONTIGNY CHANTAL 21964 11985021984021527112373.3742MSANS
+6738BLOC FRANCOIS 11964 61989071993071381911365.6242MANGL
+4323RIAS CHRISTIAN PAUL 11964 91985091990111949415051.8942MALLEM
+1244CAMBEIRON JEAN RICHARD 11964 41985051993011693613498.3742MSANS
+5905CHISSEAUX HUGUES 11964 119910719920710024 8998.6442MSANS
+5258LAMBROSCHINI OLIVIER MAURICE 11964121989051995082256617145.0642MSANS
+1452IMAM CHRISTIAN 11964 51993111993011723313653.5142CSANS
+6093DEVAUX FARID 11964 91988061994011249910473.0242CSANS
+3662MIDI JEAN MICHEL 11964 71985021992091616712994.0842MANGL
+6965PENNE ERIC 11964 619850419840410791 9503.7242MSANS
+0024FLEURS VALERIE 21965 81986101992121450311832.2042CSANS
+3927PARC CORINNE 21965 81993111992011202810240.9542MALLEM
+9121NAUSSANNES JEAN FRANCIS 11965 81988051992071616712994.4542MANGL
+6670GENIE PIERRE 11965101994051993051420611638.9242MESP
+9153DESMOULINS PATRICE 11965 11989111994092401918074.6042MSANS
+2680CHOUETTES JEAN LOUIS 11965 519891219921211302 9776.1742CANGL
+5006ESPERANCE CORINNE 21966 619940919930411389 9852.3842MCOMPT
+9852ADOUE DIDIER 1196611199401199301 7590 7564.3542CSANS
+4760MURAT CHRISTOPHE BERNARD 11966 81991041992061381911365.1342MALLEM
+4744LESCAR ERIC CLAUDE 11966 31989021988021450311830.8542MSANS
+9188HULOT MAURICE 11966 11990021991101249910472.7942MANGL
+3718SOU ANDRE 11966111991111994011249910472.9642MANGL
+1189GROSSO XAVIER 11966111995081994081868714506.1342CSANS
+7659METRA MARYSE 21966 91989051988051450311832.4142MALLEM
+1281CRIPE GISELE 21966 91987021986021527112374.7042MSANS
+7796AMIRAL ELIANE 21966 21994011992011202810239.9642CSANS
+6977NOHANT MONIQUE 21966 91986061990111949415051.2242MSANS
+7731VERGERS MARIE GENEVIEVE 21966 219941219930411389 9851.6442CSANS
+4652LAFAYETTE MICHELINE 21967 31987061988031450311831.8742MSANS
+7993CHILPERIC BRIGITTE 21967 619940519920111389 9851.0742CTECHN
+2195VELASQUEZ DENISE 219671219940519920111389 9852.2042MSANS
+5536TIFFANY CATHERINE 21967 81988051987051450311832.2042MSANS
+9529CLAIREFONTAINE CLAUDINE 21967 81991081992061267210588.8542MSANS
+1639VILLENEUVE GUY 11967 41994101993041202810241.8542CANGL
+1238BOURBESNEUR DIDIER 11967 41991091990092611219510.6742CSANS
+9384CORALLINES PATRICK 11967 31990011993031249910471.9242CSANS
+4464PICHI PIERRE 11967 2199507199407 7464 7525.8642CANGL
+6931VOL MICHEL 11967 61994051993041360611211.0642CANGL
+0488DOREE DOMINIQUE 21967 21991111990111723313653.8442MSANS
+0529RELUYEN JOELLE 21967101987101988051467711986.0142MSANS
+9178PART CHRISTIAN 11967101993061993091267210588.8942CANGL
+8565KENNEDY JOCELYNE 21968 11991011990011381911366.3442CSANS
+2088ROUSSEL LILIANE 21968 719930819920811389 9850.8342CSANS
+6684BRUN CHRISTIAN 11968 11987051992091839014313.2142CANGL
+7111VENIZY JACQUES 11968 519940319930310363 9232.9842MANGL
+9217POUTIS ALAIN 11968 21989041995071249910473.5942DMICRO
+4714MARINA FREDERIQUE 21968 21988121992011249910471.8942MSANS
+8715AULDE BRIGITTE 21968 71994051992011202810240.9142CSANS
+9713ESCUDIER PIERRE 11968 61988121993041467711986.5342MTECHN
+6341SELLEBIED MICHEL 11968 81988011994011249910473.4242MSANS
+8663JASSERON ANDRE 11968 21995081994081450311832.7242CESP
+5734ODENAS SYLVIE 21969 81991041990041381911366.0742CESP
+2288CENSIER MARTINE DANIELLE 21969 4199408199308 8359 7952.9642CINFOR
+4540PERS RENE 11969 71991111990111949415051.9742MANGL
+5166VERAN BRUNO 11969 319930719920711389 9852.3642CINFOR
+3740CLOSEAUX PHILIPPE 11969 6199312199212 9597 8729.9742CESP
+5076BELLONTE JOSIANE 2196910199312199212 7590 7562.4942.SANS
+6280MAGENTA LAURENCE ANNE 219691019930519920511389 9852.2442MSANS
+4916MONTMERLE NELLY 21969121991101992061330611017.1642MESP
+1013SEILLAN MIREILLE 21970101994021993021360611211.0942CALLEM
+3949GRUN PATRICK 11970 41993111992111330611017.1942MANGL
+0280RICHELIEU ROLAND 11970121991111992061202810241.0942MSANS
+7899FRERES CHRISTIANE 21970 71994031992011202810239.8342MESP
+5404VIARMES ROBERT 11970 41993051994011202810240.0842MINFOR
+6508CALEU FREDERIC 11970101992111991111420611637.7842CSANS
+6706VILLEMER GERARD YVES 11970 91992051991051202810240.0142CSANS
+9352WAUTHIER CHRISTIANE 21970 919921019911010668 9426.2742MSANS
+7483ARAGO REGINE 21970 11993061992101420611637.1242CTECHN
+8430BRUEYS ROSELYNE 21970 5199302199202 8359 7952.7942MSANS
+2207CAMBEFERRIS MARIE ODILE 21970 919940719930411389 9852.0642MANGL
+7014CATHERINE ANNE MARIE 21970 519930519920511389 9851.3442CSANS
+2499MONTALEMBERT FRANCOISE 219701119921219911211389 9852.4742MANGL
+0607CORBERES ISABELLE REGINE 2197011199511199411 9894 8922.7742MSANS
+8631MOUSTERIAN MICHEL 11970 519940619930511389 9852.7242CVENTE
+4299PAIX MICHEL 11970 31994011993011420611637.1142MSANS
+1722ALBERT GEORGES 11970 71990061992091202810240.8242MSANS
+7839LORRAINE DANIEL 11970 81993121992121420611639.2242MTECHN
+2390VENISE JEROME 11970 6199406199306 9597 8728.5342CSANS
+4622BOUTON SERGE 11970 71992111991111420611638.5042MALLEM
+8468MONTALEIGNE FRANCK 11970 61993031992031420611637.8042CSANS
+4926LEZARDIERE JEAN CHRISTIAN 11970 819930119920111389 9852.3842MSANS
+0318MISSIONNAI FRANCOIS 11970101994021993021420611639.2442MSANS
+2478CENAC ANNE MARIE 21971 11992071991071420611637.4442MESP
+9531FLEURIES PATRICIA ODILE 21971 6199612199512 9894 8921.9442CSANS
+2370ARTOIS MARIE MADELEINE 21971 919930219920211389 9851.2442CSANS
+9824CLAOU JANINE 219711019941019931010668 9425.7342CANGL
+0436BUISSON JOELLE 21971 519950119940110668 9426.4142CSANS
+6968RABELAIS CHANTAL 21971 619951219940110668 9426.3242CSANS
+6232CASE CHRISTIANE 21971 61994051993051267210588.8542MANGL
+5473GERANIUMS JEAN 11971 11990051994011249910472.9242CSANS
+1033ISLE JEAN PIERRE 11971 51992081994011249910473.6342CTECHN
+6691MONTAGN PIERRE 11971 91991091994011249910472.5942CSANS
+3613TRONGET FREDERIC 11971 219940419940110363 9232.4642MSANS
+5033GENELLES PASCAL 11971 81991121993061330611018.0142CSANS
+5241KENNEDY PHILIPPE ANDRE 11971 219930619920611389 9850.5642CSANS
+2276MESANGES JEAN PIERRE 11971 11991081991091202810241.0342MCOMPT
+4273HOLLYWOOD THOMAS 11971 41993021992021420611639.1042MANGL
+9172PITCHOUM JEAN CLAUDE 11971 119921019930410876 9502.9942CSANS
+2512GACHE JEAN CLAUDE 11971 81994091994011202810240.6442CSANS
+4896CHILHAC GUY 11971 219930119920111389 9850.7642CANGL
+3539MAS JEAN FRANCOIS 11971101992071991071420611637.6042CSANS
+4359GAULLE LOIC 11971 419930219920211389 9852.0542CSANS
+0794SOCLATE ALAIN 11972101994051993041360611211.0542CSANS
+6685CONSOLAT JEAN PAUL 1197211199503199304 9597 8729.5842CSANS
+3658BACON STEPHEN 119721119930219920210363 9231.8642MSANS
+5829RAINIER YVES HENRI 119721219930119920110363 9231.9642MTECHN
+5038ROSE JOCELYN 11972 41994011993011420611638.5042CTECHN
+1479NOEL JEAN PIERRE 11972 219931119921111389 9850.8342CSANS
+3997RICARD SYLVAIN 11972 61992051991051420611637.5642CANGL
+6600LOUP KAZUKO 21972 519931019920811389 9852.2042CSANS
+9876VOUZON CHRISTINE 21972 11993101992101723313654.7242CINFOR
+2373RAPAILLOU THIERRY 11972101992121994011202810240.0542CSANS
+1153RIBERA FRANCOIS 11972 3199406199306 7590 7563.9942CANGL
+0976AIR FREDERIC 119721119940219930211389 9850.6242MSANS
+1075COLOMBIER JACK 11972 6199401199301 7590 7562.5442CINFOR
+3036SOISSONS THIERRY 11972 71992021991091420611637.7442CSANS
+7541HIER MONIQUE 21972 11993101994111288210743.9942CSANS
+8819RHIN JOSIANNE 2197212199407199307 7803 7679.9442CANGL
+4699LENTISQUES FLORENCE 21972 6199501199401 9894 8923.2942CSANS
+5011CIGOGNES MARIE FRANCOISE 21972 3199605199505 7803 7681.0242CSANS
+7216FARRERE CHRISTIANE 2197212199408199308 8359 7953.2942CSANS
+9971SALVADOR REMY 119731219951219941210668 9425.9942CSANS
+6878BIGUET YVES 119731219940219930210668 9425.5842CANGL
+9980LACHAMP CHANTAL 2197312199306199206 8359 7953.7242MSANS
+3279ROITELET CHRISTINE 21973 819941219930411389 9851.8742CESP
+9903PROVENCE VINCIANNE 21973 2199411199311 8359 7953.6342CVENTE
+6941MAT SYLVIE 21973 919930719920711389 9850.7042CMARKT
+2424PLAINE ELIZABETH 21973 419950419940110668 9427.4942MANGL
+4221SOLOGNE ERIC 11973 619940619930610668 9427.2442CTECHN
+8519ERABLES DANIELLE 21973 419931019921011389 9852.6742MANGL
+7323SAUME ISABELLE ANNE 21973 919950319940310668 9426.6242CANGL
+2143CUQUES HELENE 21973 6199612199512 9894 8922.0042MESP
+5163SOULELHAT CORINNE THERESE 21973 9199407199307 8359 7953.0642CANGL
+9032PETILLAT PIERRE 11973 41992091994011249910471.7442CSANS
+9059DARONNE JACQUES 11973 419940619930610668 9426.4142CANGL
+6626DEVESSOUS JEAN CHARLES 11973 91994071993071206910241.5842CSANS
+7108BUISSONNETS HENRI 11973 419930419920711389 9851.6142CSANS
+0443FONTAINE YAN 11973 9199406199306 7464 7524.2142CESP
+3078CHARBONNIERE JEAN PIERRE 119741019931019920711389 9852.2942CSANS
+5301REYNAL JEAN LUC 11974 6199401199212 9597 8729.6742MINFOR
+7425RAFFAELLI PHILIPPE 11974 319940719940111389 9851.8842MINFOR
+9638SYLPHES PAUL 1197410199407199307 9894 8922.5742CMICRO
+9287ROSSAYS MARIE CHRISTINE 219741019960419950411389 9851.3443MANGL
+9554PILGRIMS JEAN PIERRE 11974 219931219921111389 9852.0242MSANS
+0960BOCAGE JACQUES 11974121993091994011202810240.9442CSANS
+7246PEYRIERE MARIE CARMEN 21974 419940719930710668 9426.2242CANGL
+3039COMBRAY MARC 11975 4199607199507 7803 7680.7542CSANS
+5779SAENS ANNIE 2197510199608199508 7803 7680.8942CANGL
+8044FERRY ROSINE 21975 9199507199407 7803 7679.8742CCOMPT
+0191KER MARIE HELENE 21975 3199611199511 9256 8533.0242MSANS
+6974MANCINI JEANNINE 2197511199408199308 7803 7679.7642CANGL
+0518VICTOIRE PATRICIA 21975 3199608199508 7803 7680.8042CSANS
+1262TRAUD JACQUES 11975 819940519930510668 9425.9642CMARKT
+7772MOZART SOLANGE THERESE 2197610199608199508 7803 7679.4142CALLEM
+9670GRESSETS LAUREEN 21977 8199607199507 7803 7681.0442CSANS
+8996ADRET CELINE 21977 3199608199508 7803 7680.6242CANGL
+3676MENEZ MARC MAXIMILIEN 11933 91966071994011932314935.3843MALLEM
+9549BOURGOGNE ANNICK 21936 41968051991102137316331.4643MSANS
+9677BROQUERIE CLAUDY 219381219780719910611131 9658.3743VSANS
+4456LAC PATRICIA 21938 81962041985062470018542.2243MANGL
+6693FENELON VALERIE 21939 61959121986012273717261.0743MTECHN
+0887NASTRINGUES BERNARD 11939 81962091992032137316331.2443MESP
+7338AMBROISE RENE 11940 91968041995011557012566.9943MSANS
+0858TURNEGOUET DIDIER 11942 71982011993011249910473.0043MSANS
+8537VERT PATRICK 11942 61976121993011578112722.9343MSANS
+3521HOUEL PATRICK 11942111970091995011557012568.2243MSANS
+2105ROCAL JEANINE 21944 51970071991071578112723.3343MANGL
+6696PLAIDEURS JEAN PIERRE 11945 91972121990011403411520.4243MANGL
+1309BELLE MARIE MARTINE 21945 31965011988042470018541.9743MSANS
+9127PENFOULIC EMMANUEL 11946 5198901198801 9894 8921.9943MESP
+2831CHAMAS MIREILLE 21947 21969031993031834514274.8443MINFOR
+1888BONJOUR GUY 11947 91980101993101471512024.6243MANGL
+1642POURTEL FRANCOISE 21948121974111994041471512026.1543MANGL
+5118QUINTANA NADINE 21948 6199406199304 9126 8457.3343MSANS
+7220LANDON JACQUELINE 21948 91970071994092529918967.9843CSANS
+5846ROI PATRICIA 21948 11971051991102043315669.5743MINFOR
+6443JUIN DAVID 11948 11975061991012137316331.7043MSANS
+1072BELLY THIERRY 11949 91979041992011249910472.6143MANGL
+3513GAMA VALERIA 21949121969021990071578112722.3343MANGL
+8936MARGUERITE MICHELE 21951 41973021993012017815516.0843MSANS
+9731GILLES NICOLE 21951 319961119951110668 9426.2743MSANS
+3374ROBESPIERRE CORINE 21952121973021991012781920675.1943MSANS
+9500EDELWEISS GERARD 11952 21978011992111684813422.4443MANGL
+9602PARADOU MICHELE 21952 51975051981091684813422.6843MSANS
+4313BERNEDE AGNES 21954111975041991071578112723.7243MANGL
+9347LAUZES MICHELE FRANCOISE 21956 91978061992051471512025.7043MANGL
+9922BELLA MARIE THERESE 21956 61982041981041326611018.1043MINFOR
+9508ROBERT PATRICIA 21957121979071994121911114778.0643MSANS
+9342GALLERANDS PAULETTE 21957 419950219930411389 9850.7043MTECHN
+8765ELANCOURT BERNARD 11957 71980081993061369311286.1743MSANS
+8497RIBOT JOELLE JACQUELINE 21957 21977071992121527112373.0443MSANS
+6373RENE CLAUDINE 21958121978041992021471512024.3543MANGL
+1513FRANLAINE BLANDINE 21958 91984101985041527112373.9543MSANS
+1684CHALEIL MICHELINE 21958 31978061993101471512025.3243MANGL
+2983LAVIE JACQUES 11959111988031994011249910471.9743LALLEM
+3412CORTIJO AGNES 21959 31983011982011608112915.7843MSANS
+4467VADEL JEAN MARIE 11959 51991041994011202810241.3943MSANS
+9453CAVILLON MAURICE 11960 61985111992071288210744.4643MALLEM
+7623MALESHERBES BRUNO 11961 81984081991061326611016.7443MTECHN
+1600BLANCHES MARIE CHARLOTTE 21961121990031993061249910472.0143MALLEM
+7714POMMIERS VALERIE 21962 41993081992011249910471.6143MESP
+7193CAMBARRAS PHILIPPE 119621119930119920110876 9503.2743MALLEM
+0639TANQUEUX FREDERIQUE 21962 61982081989112090215980.8243CSANS
+8276LACROIX DIDIER 11962 61991101993041467711986.8943MSANS
+5057CINO GERARD 11963 61983111995091693613498.3143MINFOR
+0238SARRAZINE MARC 11963 619941119931010363 9233.5743MSANS
+7129NIVERT THIERRY MICHEL 11963 519910419940111302 9775.7943MSANS
+8602PYRENEES DANIELLE 21964121993091992011202810240.5943DESP
+6662PUERTO SYLVIE 21964121985101984101249910473.5643MSANS
+7625SCHWEITZER LAURENCE 21964111991091990091202810240.4643DANGL
+7052CARREAUX SYLVIE 21964 61985021984021288210744.6743MALLEM
+9218SALIS ARMELLE 21965 3198801198601 8656 8146.6143.SANS
+3260JABLINES JEAN JACQUES 11965 41990091995092256617145.3043MSANS
+9721LAVOIR YVES 11965 619930119921011389 9852.1443DALLEM
+3429CAM BERNARD 11965 61987021993051839014313.5743MSANS
+1856FLAUBERT PASCAL 11965101985061994111693613498.5543MSANS
+0790JARDIN SERGE 11966101987091992101539912453.0643MSANS
+2445VIESCAMPS YANNICK 11966 11991111991101381911365.5343MSANS
+5684SARRASIN PATRICK 11966 51994101993101267210589.3943CSANS
+4794DIAZ PHILIPPE PIERRE 11966 21987111993111693613499.4543MSANS
+5479THIREUIL GILLES 11966 31992041992011202810241.0343CSANS
+6199HELEN MARTIAL 11967 91987081995011373711326.7043MSANS
+9298FOUQUET RAYMOND 11967 419960619950611389 9852.2943DINFOR
+7535COURTELINE PHILIPPE 11967 11991021994011249910471.6143MTECHN
+4595PANSEROT JEAN JACQUES 11967121989091989061539912452.5143MINFOR
+4584SANGUI PHILIPPE 11968 219950119950110363 9231.9543MSANS
+1356BERTIN MARYVONNE 21968 41993061992061267210589.6943MMICRO
+3761GARD HENRI 11968101989071993091723313654.6543MSANS
+6649GUILLAUMET LAURENCE 21968 71994021992011202810240.3543CSANS
+2548LYCEE ALAIN 11968 51993121994011202810240.1743CSANS
+0307ABBAYE ANNICK 21968 919920919910911389 9850.7443MSANS
+0524PEYRILLES YOLANDE 21969 119940719930411389 9852.5943MSANS
+3086FABRE JEAN YVES 11969 71989121994041249910473.2443MSANS
+2273BREUIL SERGE 11970 719891119920811302 9774.3843MALLEM
+5532NIRVANA FRANCIS 11970 419920219920810876 9502.1443MTECHN
+0282TOURISSE BERNARD 11970 41994081993081360611211.4743CANGL
+8554BOUVIER OLIVIER 11970 81993061992061267210590.5443CANGL
+3126PONTAROUX JOCELYNE 21970 619930619930411389 9851.1943MSANS
+0208ENCLOS DIDIER 11970 219941019950110363 9233.5443CSANS
+5629VENEUX JEAN 11970 319930919920911389 9851.7443CINFOR
+9569CASCADES MARYVONNE 21970 91991101990101202810239.7843CSANS
+4663DAUBERIE DOMINIQUE 21970 919941019930411389 9851.1543MSANS
+3912FOURNAS MARIE 21970 119930819920711389 9850.8343CTECHN
+2317CALDANA ODILE 219711019940519920111389 9850.7943MANGL
+4841MARIDOR CLAUDIA 21971 4198907199101 8656 8146.1643.SANS
+3038FERRARI FRANCOISE 21971 2199206199106 7803 7681.2543.ANGL
+3547KERA MICHEL 11971101994101993101360611209.9243CSANS
+8801ORRES JEAN JACQUES 11972 91994061993061420611638.1043CINFOR
+8995CRUCITA PHILIPPE 11972 11993041994011249910472.5743CINFOR
+1636NORVIL BERNARD 119721119930919920911389 9850.9243CSANS
+7758AURES JACK 11972 5199309199209 9597 8728.6443CSANS
+9063SATIE OLIVIER 11972111994061993061420611639.0643CSANS
+9116BREVIAIRES LAURENCE 21972 119930419920411389 9852.3343CINFOR
+3743SORALY ALAIN 11972 4199407199307 9126 8455.8443CANGL
+9641ASTARAC CATHERINE MONIQUE 219721019940619920111389 9851.6943CALLEM
+1350RAMBUTEAU RENEE 21972 61993091992091360611209.6543CSANS
+0615ROBERT REMI 11973 51994011993071267210589.9343CTECHN
+2872KERPETIT ALAIN 11973121994041993041420611637.8043CSANS
+9594BENEDICTINES JACQUES 119731219931119921111389 9851.1543LSANS
+5147TERRESROUGES DIDIER 119731119930319930311389 9852.2443CSANS
+3464CASTERAN EVELYNE 21973 519950219940210668 9426.1843MSANS
+1330INFERIEUR CHRISTIAN 1197411199607199507 7803 7680.0543CSANS
+0129DEGANNE WILLIAM 11974 91994071993071206910241.9743CMARKT
+9626STENDHAL CLAUDE 11974 51994081994121202810240.0543CSANS
+8520COUPERIN JEAN 119741119940519930510668 9425.5843CSANS
+7404PERGOLESE RICHARD 11974 31994051993041360611211.5143CANGL
+2352ECLUSE BEATRICE 2197411199608199508 7803 7680.6243CCOMPT
+6779PERRONET PASCAL 11974 91994061993061360611210.9143CANGL
+9456REVOIRS MARC 1197512199507199407 7803 7680.9943CINFOR
+9467CONCORDE CLAUDE 11975 919941019931010668 9427.4443CSANS
+7348GENOUILLY FREDERIQUE 21975 7199407199307 7803 7680.4443CANGL
+8390CEILA DOMINIQUE 11975 9199409199309 9894 8922.7243CSANS
+3308BOULANGER RENE 11975 6199410199310 9894 8922.0043CALLEM
+8061DENFERT VALERIE FRANCOISE 21975 11995041994041206910241.1243MSANS
+9867ROCHOPT MARIE PIERRE 21976 8199609199509 7803 7680.2343CCOMPT
+0466MIMOSAS CHRISTIAN 11976 1199408199308 7803 7679.3143CSANS
+6069BATIGNOLLES MARTINE 2197710199608199508 7803 7679.2843CSANS
+7049CRENON DANIEL 11947 81973041994011621213032.3044MSANS
+1304PERI VERONIQUE 2194912199310199210 9597 8729.0944MSANS
+3258DARMONT VERONIQUE 21961 21986061992021168610047.0344MSANS
+1840COQUART LUCILE 21964 41984101993051381911365.0544MANGL
+2897PERCHE ALAIN 11967101990061995011318010939.1344LSANS
+6181BIZET MIYUKI 21968 2199302199202 9597 8727.9944MSANS
+6750EDGAR HERVE 11971 21992011991011202810240.5844LSANS
+0175MARQUIS COLETTE 21974 5199606199506 7803 7680.2744CSANS
+1275FRIGOULET YVES 1197410199506199406 9894 8921.6944CSANS
+8305BEZENAC FRANCOISE 21953 2199404199304 9126 8455.7045MSANS
+4858DUPLEIX MARC RENE 11954111978121982102470018541.8845MANGL
+6314ABERDEN EVELYNE 21958 61979111978111684813423.0845MSANS
+0464PETIGNONS DOMINIQUE 11961 91984061993031527112374.9745MSANS
+3847GUICHARD PHILIPPE 11961 11983081988112090215980.9745MTECHN
+8173HOPITAL ANSENIA 21964 319950419940110668 9425.3645MSANS
+4339VILLE DOMINIQUE 11971 91994111993111267210589.1245CSANS
+1142SPITALIERI FREDERIC 11933111953121993014121428007.2246MALLEM
+6086SOEUR JEAN JACQUES 11935 91955061991053417325097.9646MSANS
+0246OREE PATRICK 11937 71965101986012470018541.1946MANGL
+7398MOUNET CLAUDE 11937 919960119950510024 8999.3946MSANS
+2438VISITATION CLAUDE 11938 31964111986012470018542.2446MSANS
+3413ETIENNE JEAN MARIE 11940121969051986072470018541.1146MSANS
+9044GUEN PAUL 11941 11973051993011518612335.2846MINFOR
+4544MONEGLIA ABDELKAD 11941121965101989072947921838.8046MINFOR
+5661BELLON PHILIPPE 11943 91965111994013200223584.4346MSANS
+4296VENDARGUES YVES 11943 31964111991012273717260.1446MSANS
+2281TRIMARAN OLIVIER 11944 81973051990011403411521.4646MINFOR
+9015IRIS YVES 11945121973011990011403411522.1346MESP
+7856BELLEVILLE GERARD 11946 31965101986012470018540.2946MSANS
+9041SADI PIERRE 11946 61977121995062000715437.9146CINFOR
+2976BLANQUI GIANCARLO 11946 51968091986072273717261.9446DSANS
+4370GROUPE PHILIPPE 11947 21974071990011403411522.0946MTECHN
+7697SEYSSINS BERNARD 11948 51968071991012470018541.8646MALLEM
+9305SEINE GUY 11949 91970041987012273717259.9046MANGL
+5926BOILEAU BERNARD 11951 61972051989012273717261.9046MALLEM
+3592PETIT GILLES 11951101974011995032547019085.7046MANGL
+7590PLAYA JEAN YVES 11954101977091982102470018541.4746MSANS
+8144DARIE JEAN PIERRE 11954 21995011994011420611638.9246MSANS
+8637GUILDFORD PHILIPPE 11955 21974061995082547019085.8646MINFOR
+7809CEYRESTE FRANCIS 11955 31975021993081608112916.2546MSANS
+9226MONDONVILLE MICHEL 11956 51979011992011471512025.1146MTECHN
+8675BLANCHISSEURS ANNIE 21958 11979071993041471512024.3346MSANS
+0338BARBUSSE ROSELYNE 21958 1198902199209 9597 8729.2546MSANS
+4849SANZILLON MARIE NOELLE 21958 81979101978101288210743.8346MSANS
+7791PRA BRIGITTE 21958 81981041980041684813422.0846MSANS
+3255LENOTRE BERTRAND MARIE 11959 81980111991012022115555.6246MESP
+3148EOLIENNE GUILLAUME 11959 11983031991011868714507.9446MSANS
+1942CAUSSES MICHEL 11960121987081991061288210745.4746MSANS
+8983RAYMOND JEAN LOUIS 11961 11985031991011868714506.7246MSANS
+5296BRETAGNE PHILIPPE 11961 41983081991011868714507.7246MSANS
+0323QUONIAM GILLES 11961121983051993091450311831.5746MSANS
+3231MOULIN HUGUES MARIE 11961111985091995011373711328.2246CSANS
+9926PIN PATRICIA 21962 41984051983051288210745.7546MSANS
+0400HARAS PHILIPPE 1196312199407199411 9597 8728.1746MANGL
+8313ROUILLAC BERNARD 11964 31991021991022529918968.4746MSANS
+1947CORNEILLE DOMINIQUE 11965111987021994091693613499.4346MANGL
+1364KERSAUX JACQUES 11965 41987091986092090215981.4746CANGL
+4571CAILLOU ERIC 11965 91985041992071616712995.9946CSANS
+5043BAC MONIQUE 21965 61988081987081539912452.4646CSANS
+5037CALLE PHILIPPE ROGER 11966 81994091993091839014312.4546LSANS
+3072PONTHION ALAIN 11966 21990091989091467711987.1346MANGL
+3441ARMAINVILLIERS YANNICK 11966 11987021994091693613498.8546MANGL
+2298PLESSIER ANNE MARIE 21967 2199004199111 8656 8146.6146.ANGL
+3281ALBAN ELIANE 21967 21986111992011288210744.0846MSANS
+1984ROCH GERARD 11968111992031992041467711986.7246MSANS
+6981TROQUEREAU JEAN JACQUES 11968 91992081991081467711986.2646MALLEM
+4384LAMORLAYE MICHELE 21968 31993121992011202810240.1746MSANS
+4391JUGE BERTRAND 11969101992091993072397418036.9346MSANS
+4122CAULAINCOURT YVES 11969101988081987081539912452.1646MINFOR
+5356LACAPELLE ALAIN 119691119930419920411389 9852.0246MTECHN
+8278CHANTEMERLE GERARD 11969 919920619940710876 9503.4546MSANS
+2596DESSUS GUILLAUME 11969 11991091990091467711986.4946MSANS
+7596MEYERBEER EDOUARD 11970 11992111992111330611018.1046CMARKT
+5724HOCQUETTES AUDE 21971 31991071990071467711986.3146MSANS
+1372AUBEPINS CHRISTIAN 11971 21994101993101360611209.8846MALLEM
+8783BAUDOIN JEAN FRANCOIS 11971121993081994011202810240.4146CSANS
+3416MIREFLEURS NORIKO 21972121994021993021360611211.5646MCOMPT
+0948TIERRAINE MARC CAMILLE 11972 319930719920711389 9851.5946MTECHN
+0904FOUGERES JEAN MARIE 11972 919920119920311389 9850.8946CTECHN
+4010QUARTRAVEL CATHERINE 2197211199501199401 9894 8921.7246MSANS
+1528FARMINGTON RENEE 21973 119940419940111389 9851.0346MSANS
+8402TREZEE GILLES 11973 919930719920711389 9850.8846CSANS
+7279CORNE GUY 11973 71994011993011360611210.3746CSANS
+0232PIN JEAN CLAUDE 11973 519960619940110668 9425.4546CTECHN
+9635MAGALI CHRISTIAN 11974 4199607199507 8656 8147.2246CALLEM
+7035ANGLAS LUCIENNE 21932 21963011988102273717260.1448VSANS
+7818HERZOG DOMINIQUE 21937121972101993041557012567.0848DSANS
+6478MAREYE JEAN PHILIPPE 11940 21959101983102273717260.8048MSANS
+1661VAN EDITH 21940 81974091990012137316330.9748MSANS
+1597MATRAT CATHERINE 21940 61967071992121834514275.5948MTECHN
+1046MAUNY JEAN CHRISTOPHE 11941 81979021994011471512025.4748MSANS
+8249FILLES MICHEL 11942 51962081993093417325097.1948MSANS
+0711PICCINI JEAN JACQUES 11943 81963041995072470018542.0048MSANS
+0378HALLIERS JEAN PHILIPPE 11943 3199501199401 9597 8729.3648CALLEM
+9597VOLTE MARIE THERESE 21946 71968041990012273717260.9448MCOMPT
+4768PICAUD BRIGITTE 21947101973101995102043315670.9248DSANS
+0554ROLAND ANNE 219471019920719910710024 8998.7748DSANS
+3349SERRE PIERRE MARC 11948 61969021991012470018542.1048MALLEM
+3355KURFURSTEN PAULE MARIE ODILE 21948 7199501199301 8656 8145.6648MSANS
+6449IRUENA DOMINIQUE 11949 71969031990051932314934.2748MESP
+3922LAVERSINE YVES 11949 81975101986103634926571.4148CSANS
+7286JOUVENET HENRI 11951121981021994011621213032.6548MANGL
+2199CABOTS CHRISTIAN 11953121988121995011373711326.7148MINFOR
+8300CHARPENTIER CHANTAL 21955 61992011993041249910472.4148MSANS
+2397FAMILIAL JANY 11955 61982101994041471512024.3348MANGL
+9021BIED CLAUDE 11957 51980061992111911114779.6848MSANS
+4703COZ JEAN 11957 41979081993121911114779.7748MSANS
+3297STALINGRAD BERTRAND 11959 31981081992102090215980.9748MANGL
+2319FRANCOEUR HENRI FELIX 11959 11980081993121804514081.0148MSANS
+8833MONTLHERY ELIANE 21960111983041989041527112373.6848MANGL
+1084MAGNARD JEAN PIERRE 11960 91984021992012022115556.0248MSANS
+4562HAUTE ANNIE 21961 61984061983061249910472.8748MTECHN
+4907FRANC BERTRAND 11964 21984081986072090215980.5648CSANS
+2492ETANG PASCAL VINCENT 11968121990111989111467711986.6248MANGL
+3754RAOUL FRANCOISE 21968 91994031993091450311831.8448MTECHN
+7902CELARD BERNARD 11969 31990041989041467711987.9048MANGL
+0316FLORENTIN TYRONE 11969111989081989021450311832.4148MANGL
+1981DEPARTEMENTALE PIERRE 11970111993041992041420611639.0148CSANS
+4735BEL SKEVOS 11970 61991021994011249910473.5448MSANS
+0999CONNE DENIS 11971101993041992041420611638.8848CSANS
+3832BEACH SERGE 11973 41994121994011202810241.1248MTECHN
+4697PURPAN FRANCK 11935 51976041994011578112723.1550MTECHN
+1565NEMOURS ANDRE BERTIN 11939101967071990091932314934.3150MANGL
+8977MAHATMA HERVE 11942 41981051994011621213032.0650MANGL
+8738ZILINA JEAN LOUIS 11942101980061990031249910473.4750MTECHN
+6415ECUREUILS ERIC WILLIAM 11942 61969081994011932314934.7050MSANS
+9332FRIRION GEORGES 11945 5199409199309 9597 8729.4950MSANS
+5001YVES PHILIPPE 11945 41975111988071578112722.8750MSANS
+2290ENSOLEIADO JEAN MICHEL 11946 21988051994011288210745.5250MSANS
+8492LAGRIVE LAURENT 11947 81982011993011369311287.2950MANGL
+3609GUESDE ERIC 11949 11989061992031249910472.1950MSANS
+8482TOUT ANDRE 11949 71980041995011621213034.2250MANGL
+8866PERCENEIGE PASCAL 11950 41976071995011578112723.7450MSANS
+3195VIVE CLAUDE 11950 21981081995011621213032.2050MSANS
+2538AICARD SABINE 21954121976061993031608112915.5150MTECHN
+5185LUTHEZIEU PATRICK 11955 51988021994011288210744.2650MANGL
+3135EDER DIDIER 11957 819880119930310791 9502.9150MTECHN
+9006MAURETTES JACQUES 11958 11985041993011369311286.6950MINFOR
+8049COLLIERES CHANTAL 21961 11984031983031288210745.2250MTECHN
+8766TERROIR GILLES 11963101987121989091450311832.4150CALLEM
+2924LAC BRUNO 11963121989051988052090215981.1450MSANS
+5549DUMONT MICHEL 11964 51988051991081450311832.4250MALLEM
+8382MONSEIGNEUR CHRISTIAN 11964111992011994011249910471.8950MSANS
+8129STATION BRUNO 11966 41994051993051202810239.8750MANGL
+3420EMERIAU JOSE 1196711199306199206 9597 8729.8450MANGL
+9831CLAMOUR YVES 11967 819950919940910668 9427.4950MANGL
+9072MENDON GUY 11968 1199605199505 8656 8146.8850MSANS
+4669FAURE ERIC 11968 3199505199405 9126 8455.8850CTECHN
+4319MOURISCOT PATRICK 11970 9199306199206 9597 8729.2750LSANS
+1731FRANKLIN MICHEL 11970 4199401199301 9597 8729.4050CSANS
+0510PILOT DANIEL 11972 519961219951211389 9851.5250CSANS
+5973LECLERC MAURICE 11974 3199606199506 8656 8146.3750CSANS
+9358MONBEL HENRI 11933 91959031993011557012567.1251MVENTE
+7641BRETONNE CHANTAL 21936 51962081990012273717260.9451CSANS
+7048CASTELNAU LAURENCE 21937 91962031991042273717259.7851CMICRO
+5481PASCAL GILLES EMILE 11937101958051979013327724476.1651MSANS
+6723SARRAZA FRANCOIS 11938 21962031989082572519240.3551MSANS
+6355REPOS JEAN MARC 11938 41960011980013327724476.5251MSANS
+1644ACARDIE BEATE 21939 31963091991072273717260.2251CTECHN
+5030LOGELBACH ASTRIDE 21940 11962031991042137316332.4551MVENTE
+5298GARDE MARIE FRANCE 21940 91969011989011621213034.1951MALLEM
+6400SIONIE CATHERINE 21941 3196206196106 4894 6128.0051MTECHN
+5537CAPEOU LUCE 21942 519931219920110024 8999.7951DINFOR
+7143DEO NOBUKO 21942 41969091991102137316331.9751MSANS
+9555CAMBRONNE DANIELE 21943111962051984122572519240.2851CSANS
+3792EAUX JEAN 11944 81966031995072137316330.9751MSANS
+6932PAULIN ISABELLE 21945 41972041989011621213033.2851MALLEM
+1308CHRISTOL MARTINE 21945111968121993061834514274.8451MSANS
+2877DANIELLE GERARD 11946111970051992011471512025.1151MSANS
+5367ILE FRANCOISE 2194712199010198910 8656 8146.1451.SANS
+3253VERG BIRGIT AGNES 21948101969071985042470018540.3951DANGL
+5806FRED CATHERINE 21948 41968041990042572519239.8251MANGL
+6174BANQUIER CATHERINE 21950111971061989011621213033.7051MSANS
+8732FOUNSUT PIERRE ANDRE 11950 11978071995041249910472.1051MCOMPT
+4803ALASSEUR AGNES 21950 71974051992091471512026.1051MSANS
+9024CHATARDS CAROLINE SOPHIE 21951 31972081987052334017609.4651MALLEM
+1259SOUFFLOT FRANCOISE 21952 81981011983091326611017.2551CSANS
+7009PARADIS LUDWIKA 21952 71974011973011215710279.1951MSANS
+9186LESPIAT EVELYNE 21952111973041990101578112722.1551MSANS
+6834FLEMING JOCELYNE 21953 41973021993012017815517.2851MMARKT
+5164BEAUVILLAGE COLETTE 21953 91986081985081684813421.4051MSANS
+4947ESCALUS CHANTAL 21954 219810519800510024 8998.4151MSANS
+8461MARGUERITES CATHERINE 21955111976061994051471512025.0151DSANS
+5391FOURCAULT ANITA 21956 61978061992091471512024.4451MSANS
+2777ALMUNECAR CHRISTINE 21958 41993071992011249910471.6251MINFOR
+9747DROPT MONIQUE 21958 31979031993041471512024.0851MANGL
+2758CRAYS KIM LEE TAMARA 21959 11992031991011202810240.1951MSANS
+8238SERGENT ISABELLE 21959111983061982061326611016.0351CSANS
+4402DIVISION SABINE 21959 71979101993071471512025.1651MALLEM
+1231FACE HUBERT 11959 319950319940111389 9852.6751LTECHN
+3907ESBLY GUNDA 21960 81980071979071288210745.3451MANGL
+4821TAMEYE MERCEDES 21960 119820919810910668 9426.6651MANGL
+1175CRESY CHRISTIANE 21961 21990111989111249910471.6651SSANS
+6825FEUILLES DENISE 21961 51982051981051326611016.0651MANGL
+5368FLANDRE SUZANNE 21962 71983111982111249910472.3351MANGL
+0947LORRAINS CHRISTINE 21962101985041984041288210745.1651MALLEM
+5318CLAMART ANNE 21962 51982081981081326611018.0051MSANS
+8693FILLIETTE JEANNINE 21962 31982091981091194310201.7051MANGL
+5069GRAVELLE ILSE BERTI 21962 81992061991011202810241.9051CSANS
+5908VERTS CATHERINE MARIE 21962121984111983111608112915.9651MSANS
+3068DUNKERQUE ELIANE 21962 31986101985101202810240.4051MSANS
+5894CARESTIER MARYSE 21963 41985091984091288210745.1351MALLEM
+1739HILARION LAURENCE 21963 61982091981091497412179.3651MSANS
+8326MASSAY MARTINE 21963 41984091983091608112915.6951CANGL
+6923GUYONNE NATHALIE 21963 81983101982101608112915.7551CVENTE
+3533SAVIGNAC SERGE 11963 319890319880310407 9271.2351MSANS
+9590GARIBALDI FLORENCE 21963 519940619920111389 9852.0051CANGL
+5287HORIZONS MARIE ODILE 21963 21983021982021249910471.5651MSANS
+9152PARRERE PHILIPPE ANDRE 11963 61992021991021381911366.2151CSANS
+4730CROISETTE MARC 11963 61987111992012529918968.2051MANGL
+7896LONGUE MARTINE 21964 91986041985041288210745.7251MSANS
+0156CHTANOU YVES 11964 71993011992011839014314.0851MANGL
+3013CELLIER REGINE SYLVIANE 21964 719870619860610668 9426.4151MANGL
+1040HAUTI MARTINE 21964 11984101983101288210745.3951MINFOR
+0730COURBET CHRISTINE 21964101984101983101202810241.7051MSANS
+2393ROTONDE ELISABETH 21964 71984071983071202810241.8451MANGL
+6940LELIEVRE RYOKO 21964 21985051984051527112373.2351CSANS
+5571MARCEAUX ANDREE 21965 31993091992011202810240.2251MSANS
+9937MANDARINE GHISLAINE 21965 11994031992011202810240.9151MANGL
+6185HERMITTE FRANCOIS 11965 91990051989051949415051.5051CSANS
+5405ARTZAMENDI MICHELE 21965 71989081988111949415050.6751MANGL
+4260MIROMESNIL MARTINE 21965 71988031987031450311830.5351CSANS
+9396BARBUSSE MICHELLE 21966 91986061993011839014313.0951MSANS
+8592PLANTIERS MARTINE 21966 71994031993031267210589.5751CSANS
+6905HAVRE PATRICK 11967 61988111987112090215981.2251MSANS
+0499BEAUMARCHES JEAN PIERRE 11967111990021994051839014313.9551MSANS
+9812PECHERIE FLORENCE 21967 219940419920111389 9852.2251CESP
+6177RIQUET JANINE 21968 51994031993031267210589.6651CTECHN
+1459FRANCE ANNE MARIE 21968 21988091993011381911366.4951CTECHN
+9340EPINE RENAUD 11969 11994081993081206910239.8651MSANS
+7293OBERKAMPF JEAN JACQUES 11969 81989041993041249910471.7051CCOMPT
+0852GUERARD MARIE SOPHIE 2196911199308199208 9894 8923.1651MESP
+8275DELZONS MICHELE 219691119930319920311389 9852.7251CALLEM
+5873MARCHE MARIE LAURENCE CHRIS2197011199205199105 9597 8729.3051MSANS
+8465BERNASSE ISABELLE 219701119920319910311389 9852.6551MANGL
+2444BOUGNON MARIE JEANNE 21970 81993111992111267210590.7451CSANS
+3440BAZOCHES MICHELE 21970111993101995041206910240.4151MSANS
+2864GOAS SERGE 11970 51992101993081267210590.4151CESP
+5499CLOHARS GERARD 11970 819900819890810024 8998.8451CSANS
+6120MARVIVO EVELYNE 21971 519950219940210668 9426.5351MSANS
+5419BEZANLEU CATHERINE 21971 51994031993031267210590.2951MTECHN
+0309TUILERIE SOPHIE 21971 21994051993051267210590.6951CSANS
+6131CAGNES NICOLE 21971 519940619930610668 9426.5351CCOMPT
+8518VILLOUE JOELLE 21971 61992041993051267210590.0951CSANS
+8453JONCHERE DOMINIQUE 11971 319940619920110024 8998.7651MSANS
+5604NEUILLY AGNES 21972 119920419910410668 9425.8151MSANS
+7047ALOUETTES MARIE ANDREE 21972 419920119910111389 9850.9451CSANS
+5306BOPENIAN MONIQUE 219721019920919910911389 9851.9151MSANS
+9169FLEURIS LILIANE 21972 71993121992121267210588.9251CANGL
+8308BUZENVAL VERONIQUE THERESE 21972 5199607199507 7803 7679.4151CALLEM
+1210NAUGAIRE MARIE ANTOINETTE 21973 61993081993091267210589.5252MSANS
+5105HUGO CATHERINE 2197311199607199507 9894 8921.4651CANGL
+3726LIVRYS BRIGITTE 21973121995071995051288210745.4751CSANS
+5859MUZY PASCALE 21973 619930719920711389 9850.6151CVENTE
+1436SAUVE DOMINIQUE 21973 61994101994121206910241.1351MSANS
+2239CHALIGNY KYOKO 21973 31994091993091206910239.8651CSANS
+0183LAURIERS PAULE 21974 81995111994121206910241.2551CSANS
+9366PINET LAURENCE 21974 819940819930810668 9425.3651MESP
+6833VIGNY EVELYNE 21974 51994081995121206910239.9952CVENTE
+1571SEINE GUY 11974 3199609199509 7803 7680.8951CANGL
+8078LAMARCK JOSIANE 21974 3199602199502 9894 8922.4151CTECHN
+0018LOUVERSEY CATHERINE GINETTE 2197510199611199511 9894 8922.1451CSANS
+7419VEROCE MICHELLE 21975 3199406199306 7803 7680.1451CSANS
+8512ASSOMPTION CHRISTIANE 21975 3199608199508 7803 7680.5951CVENTE
+6365EDENFLORE BRIGITTE 21975 1199308199208 7803 7681.4251.ALLEM
+2274LOTUS ISABELLE 21975 7199612199512 9894 8923.1351CSANS
+5674MAUBLANC NICOLE 21975 9199602199502 9894 8922.8051CSANS
+0762GIORDAN ANNE MARIE 21975 1199607199507 9894 8921.3151MINFOR
+0696PICAUD JEANNINE 21976 2199607199507 7464 7525.7451CSANS
+6681MARCEL FRANCOISE 21976 3199608199508 7803 7680.6251CSANS
+0740CHESNAIE PHILIPPE 11977 1199507199407 7803 7681.2551CINFOR
+4654CANNES FRANCIS 11977 3199609199509 7803 7679.5851CANGL
+7430MULLERON FRANCOIS 11978 5199607199507 7803 7680.8451CSANS
+3935LISSAND ISABELLE 21978 4199607199507 7803 7680.6351CSANS
+5586SCHUMAN NANCIE 21931111974111991071578112723.0552VSANS
+1401RENAUDOT MARYSE 21933 51968071990071621213033.8152MSANS
+1061AMBOISE DENIS 11934 11964041993011663613344.2752MSANS
+9799GUILLEMETTE JEAN CLAUDE 11935 31955021991082470018540.7552MSANS
+3145SABLES PHILIPPE 11936 11959021993073417325098.3651MANGL
+3589FOURMONT PASCAL 11936 71959031982063327724477.0651MSANS
+2996PAUL MARIE PIERRE 21936 51972121989011621213033.9152MSANS
+2578PISY YVES 11938 71962111981102470018542.2252MSANS
+1257LAMARK PATRICK 11939 71963121992052470018541.5052MINFOR
+6677BRETHENCOURT NOEL 11939 21972111991011578112722.7052MSANS
+9734FIGOURNAS DENIS 11940111971041989101932314934.7252MANGL
+6502POMMERY ROBERT 11941 81963121993011663613344.0552MSANS
+2718CHENES HAFID 11941 11964081994011557012567.3952MSANS
+4104LANCY LIONEL 11942121988011995013587826261.1652.SANS
+8284MERY PAUL EMILE 11942 61968021994012137316331.8752MCOMPT
+3992BOUTICOURT EUGENIO GIOVANNI 11943 51964071987042273717261.8452MALLEM
+9819LATTRE GUY 11945 31974071992071369311286.1252MESP
+1150CAILLOUX JOSIANE 21945 11965021985042470018541.4752CANGL
+6760HAUTINS BRIGITTE 21945 719781219900711131 9659.0652MSANS
+2900FOS BERNARD GUY 11946 91974051993091168610046.6952CINFOR
+3096CASTELLAR JACQUES 11946 71972041991112137316331.0352MINFOR
+3983FLEURIE ISABELLE 11948111973111994011663613343.3052MANGL
+9627CITROEN SILVIE 21948 31970041991012990722150.2852CALLEM
+2626BUFFON BRUNO PIERRE 11948 51974051993011621213032.8352MANGL
+1995GRENELLE PASCAL 11949 81975041991071578112721.7152MSANS
+6675EMAN BERNARD 11950 71973081991012739020403.1152VSANS
+1203JACOB JEAN PIERRE 11950 619790819780811389 9852.0052MSANS
+1502COUBERTIN MICHEL 11951121979031992121608112916.4752MCOMPT
+9762APPOLINAIRE JEAN CLAUDE 11951 51971101992112175716600.9252MANGL
+6014MAURICE JOEL 11952 419910319900310024 8999.7952CSANS
+8272BESANCON CHRISTIANE 21953 2199304199204 9597 8728.9452SSANS
+4121VOLTAIRE PHILIPPE 11953 51972071995122043315671.2452CALLEM
+3385CARNOT HENERE 11953121978061993051864214469.1452MANGL
+9459SAUVETERRE OLIVIER MICHEL LOUIS11954101982061990011608112916.2352MANGL
+0260MOURILLON JOELLE 21955 21984011992011249910473.0952MSANS
+6808ROBERT PIERRE 11956 61979061992021471512025.3752DVENTE
+1754CITADELLE PHILIPPE 119561019830619910311131 9658.9152MANGL
+3114PINCES FREDERIC 11956 61976031988011684813422.5952CSANS
+2401PAPE MARIE ANNE 21956 31978061992121471512025.9552MSANS
+3724AGNOS ERIC 11956 71976041993111578112721.7052CSANS
+1438KERLIBOUZEC EVELYNE 21956 71981111980111527112373.3252MINFOR
+8247VERSAILLES LAURENT 11956 61980081995091249910472.5552MSANS
+0939FLAMBERTINS ALAIN 11957 819950619920110024 8999.0752CSANS
+5765FOCH ROBERT 11957 11981031993031608112916.3252MALLEM
+9301GARDETTES DOMINIQUE 21957 1199505199405 9126 8456.6952MSANS
+8571TIRE ROBERT 11958 11984051992041326611017.9752MSANS
+7487THORNTON JEAN LOUIS 11958 31981041994051804514081.0752MANGL
+3406AVEL PAULE 21958 419780619770611515 9930.5352MSANS
+0124DESPORTES STEPHANE 11959 21979021995091471512025.7252MSANS
+6928ASSIER PIERRETTE 21959111993101992011202810241.1352CTECHN
+0989FEROLLES WILLIAM 11959 81983041982041326611016.3052MSANS
+9802ORGE JEAN CLAUDE 11959 11980081987112256617145.8752MSANS
+8376FOUQUE ERIC 11959 2198801198502 8656 8145.6552.SANS
+8925COURBET YVES 11960 71987031989112090215980.8752MALLEM
+3897ANDELU ALAIN 11960 81991011994011249910471.7452DSANS
+3323BOSQUETS CLAUDE 11960 41983031995121433211715.6952CANGL
+7089COULOUBRIER DENIS 11960 7199303199203 9126 8455.6552CVENTE
+3026AGUILLON LUC 11961 2199601199501 8656 8145.6652MANGL
+9585EGLISE ANDRE ROGER 11961101988111993092401918076.1152MSANS
+0634MAINE JOSEPH 11961111986011992121288210745.7052CSANS
+7284FORTES ANDREE 21961 51991111990111949415051.2152MSANS
+3518VIELLE GEORGES 11961101988091992121249910473.6752MSANS
+3969FERIGOULAS SYLVIE 21962 71983011982011288210743.9052MALLEM
+1749ARCADIA PASCAL 1196210199305199209 9597 8727.8352CANGL
+5023PAVIS YVON 11962 4199512199501 9126 8455.5352MALLEM
+6853PLAGNE ROBERT 11962 819840619830611131 9657.1252CINFOR
+0421HARAVILLIERS MAX 11962 819850819840810791 9502.9852CINFOR
+8436PLEIN ROGER 11962 51984081989112090215981.3652CESP
+2433SARTRE SERGE 119621219850719920910791 9502.6652CSANS
+4413WALDTEUFEL FRANCINE 21962121982051988051608112916.0252MANGL
+2214MONTEZE FRANCOISE 21962 3199302199202 9597 8728.3752MALLEM
+0233COMMUNE ANNE 21963 2199312199212 9085 8419.4452STECHN
+0441SOLARIUM ELISABETH MARIE PIER21963 21993071992011202810240.9952MTECHN
+4510LETELLIER HUBERT 11963 31986111986031527112374.5852CSANS
+1778EVEQUE BERNARD 11963 51986111992011949415051.3952MSANS
+1339SEGUR SERGE 11963 91985091992121527112374.1252MTECHN
+4092HEURSAULT AGNES 21963 51982031981031326611016.7552MSANS
+9379GOJA MARIE THERESE 21963 71983041982041288210744.2652MINFOR
+1533BODIN ISABELLE 21963 419831219821210024 8998.9152MSANS
+9862FOSSE BETTINA 21963 91990111989111949415051.9552MSANS
+6255VIGNE NICOLE 21963101984111983111608112916.9152MSANS
+6101VAUGRENIER CATHERINE 21963101984051989041527112374.9452MINFOR
+8653SEQUOIA HENRI 11964 51988031994011249910473.2052MSANS
+9919BILY PATRICK GUY 11964 91985091992091949415050.6352MSANS
+7059VALLEROY BEATRIX 21964 2199204199104 9597 8729.0752MSANS
+5440PORS ANNE MARIE 21965 21986061985061527112373.1752MSANS
+2587VICOMTE PATRICIA 21965 11991111992011202810239.8652CESP
+9598SULLY CHRISTIAN 11965 91994051992011202810240.1452MANGL
+0899MANU JEAN PAUL 11965 519880519870510407 9270.2952MTECHN
+0493POET JEAN PAUL 11965 119891219881210407 9270.5652MINFOR
+7853LILAS JEAN CLAUDE 11966 31986051985051288210744.5052MANGL
+4637LONGCHAMP CATHERINE 21966 91988051993091839014313.0952MSANS
+5141LINDFIELD GERARD 119661119870119860110407 9272.2452MANGL
+3945PESSOT VERONIQUE 21966 119910719900710024 8998.8952MINFOR
+6408PUJOLS CHRISTINE 21966 71987061986061249910473.0952MSANS
+5883AUREA JEAN 11966101988061991011249910471.9452CESP
+6385CHAILLY LAURENCE 21967 419910719900710024 8998.4152MANGL
+8609CEINTURE ANNE MARIE 21967 21991121995071202810240.1452MANGL
+1367NOIRES ANNIE 21967 61991101990101330611017.1952MSANS
+6827NOISY FRANCIS 11967101988011987011450311831.9752CESP
+3823RESISTANCE CHRISTIAN HENRI 119671119890319880610407 9270.8452MSANS
+5716CYRAN PATRICK 11967 11987071994111450311832.6852MANGL
+2307ARBELLARA NADINE 21967 61988091990111839014313.9352CSANS
+0224VEBRET VIRGINIE 21967 9199406199305 9126 8456.1652CANGL
+3639BERGERON CHRISTINE 21967 619951219940110668 9426.1852DSANS
+5292CREGY MAURICE 11967 719940819930411389 9852.4952MANGL
+8369BRENS COLETTE 21968 81991101990101330611017.4152CANGL
+2447REBEYROLIE ANNIE 21968 619900319900911389 9851.0352MALLEM
+0478MES CHRISTINE 21968 4199309199209 9597 8728.7152CSANS
+2875CLION MARIE FRANCE 21968 319930519920511389 9852.4552CSANS
+8148CROS FRANCOIS 11968 41988111987111249910472.7352MSANS
+9990VERCINGETORIX CHRISTINE 21968 119950419930411389 9851.2852MSANS
+2796BLONC JEAN PIERRE 11968 4199301199511 9597 8727.8152MSANS
+9996GUIS LUCIEN 119681219931119920110024 8998.5352CSANS
+7769NERVAL MICHELE 21968 41990051994011330611017.8352CSANS
+6262RACINE ALAIN 11969 719890419880410407 9271.9552CSANS
+9102SAUGES BENOIT 11969 11989071993081381911364.8152MSANS
+4448NARON PASCAL LOUIS 11969 2199605199505 8656 8145.6952LSANS
+3197CHAUMES GUY 11969 819930619920610024 8998.9452MSANS
+2201LUYNES SOPHIE JOELLE 21969 5199311199211 9597 8728.2352CANGL
+3000PLAINES JEAN MARC 1196910199204199312 7677 7604.9052CINFOR
+7547BREGUIERES THERESE 21969 719920419910411389 9851.3352MANGL
+9804CADOT MARIE JOSE 21969 81991101990101330611018.2252MSANS
+5321JAUNAC SOPHIE 219691019910719900710024 8998.5852MSANS
+3824LAVISSE DANIELLE 2196912199405199305 8359 7952.9252CSANS
+6505LOUISE PIERRE 11969 419910119900110024 8999.8152CSANS
+9873BOULOURI JEAN MICHEL 11969 71990021992091202810241.3652MSANS
+9771BREUILHSUD THIERRY 11969 419910719900710024 8997.9652CCOMPT
+8931ELVARD CLAUDE 11970 81994031993032107316096.9852MANGL
+2755GEROME MAURICE 11970 8199311199212 9597 8728.9552MTECHN
+8557CYRNOS BERNARD 11970 5199404199201 9597 8729.9052MSANS
+3651COCTEAU GABRIEL 11970 41994011993011267210588.5352CSANS
+4459VITALIS ALAIN 1197010199612199512 9894 8921.9152LSANS
+9388COURTENAY JEAN JACQUES 11970 91990031995071202810241.1252MVENTE
+1310CAILLADE ADRIEN 11970101996111995111616712994.7752CALLEM
+5461GOMETZ CHRISTIANE 21970 61994071993071407611559.6252CSANS
+0669MAUVALAT DANIELE 21970 6199209199109 9597 8728.7652MSANS
+0103TAILLEFER MICHELINE 2197010199608199508 7803 7680.0552CSANS
+5577MOUGINS CHANTAL 21970 619940919930411389 9852.6952CSANS
+0340LAMBESC MARIE CHRISTINE 21970 3199607199507 7803 7680.6252CSANS
+1328PIERRE PATRICE PAUL 1197111199106199006 8656 8145.0352.SANS
+3172DENOUVAL DANIEL 11971 419940319920110024 8999.1652MSANS
+9639ESPAON MARC 11971 7199407199307 9597 8727.7852CTECHN
+6612FONTMERLE JOELLE 21971 919930419920411389 9850.8452CSANS
+1855MAILLOL PIERRE 1197110199303199203 9597 8728.0452MSANS
+8791RASTINES PHILIP 11971 81993011992011267210589.2852CSANS
+2880FLORALIES MARIE THERESE 219711219930919920911389 9852.3352CINFOR
+9921SERPENTINE FRANCOISE 21971 419920219910211389 9851.1652CVENTE
+6761KING JACQUES 11971 5199606199401 9597 8729.9952CALLEM
+8677BREANCON VERONIQUE 21971 919940819930810668 9425.7252MANGL
+0030SUVERET PATRICK 1197110199501199304 9597 8728.6652MSANS
+4284REGNAULT JEAN MARC 11971 719900619890610024 8999.3652CSANS
+3275POMPIDOU DIDIER 11971 6199610199510 8656 8145.3352MSANS
+7270BONNEUIL MARC 1197110199107199007 8656 8145.3052.SANS
+2988DEREURE BRIGITTE 21971 8199606199506 8656 8145.8752CINFOR
+0334GRAVESONN JEAN 119711119911019901010024 8997.7852CANGL
+5425PONT FRANCOIS 11971111995041994041267210588.8852CSANS
+7132MIDI JEAN PIERRE 11971 5199608199509 8656 8145.4852CANGL
+5172BLOMET JEAN PIERRE 11971 81993061992061267210589.5252CSANS
+1753QUIETA DOMINIQUE 11971 41991061995101202810241.7652MSANS
+8062DOMINO MARC 11971 719910819900810024 8999.4052MSANS
+8636ECLUZELLES MICHELE 21971 9199609199509 9894 8921.5952CSANS
+5812REPUBLIQUE VERONIQUE 21972 31995101994101206910240.4052CSANS
+0712TEYSSIERES MARTINE 21972 6199607199507 7803 7680.8952CSANS
+3543DUNETTE LAURENCE 21972 4199608199508 8656 8145.9152MSANS
+7967FOUCANCOURT ASTRID 21972 219930619951011389 9850.8852CSANS
+6876THERESE MISAKO 21972 719940819930710668 9427.2652MSANS
+9406GALLIEN MICHEL 11972 219910719900710024 8997.9052MSANS
+7605BAUMES ALEXANDRA 11972 7199501199304 9597 8728.1952MALLEM
+1625ARTHUISIERE JACQUES 11972 61995011994011206910240.6652CSANS
+6717DOUMER PHILIPPE 1197212199611199401 9597 8728.8052CESP
+1677CHABRIER LAURENT JEAN CLAUDE 1197211199504199404 9126 8456.3952CSANS
+8299PUY JEAN 119721119931219940111389 9852.6352CANGL
+8443COUSTERES MICHEL 11972 2199107199012 8656 8145.4452.SANS
+7741NANT FREDERIC 11972 31991061994111202810240.7652MSANS
+0657TAUDE FABRICE HENRI 11972 319940219930211389 9851.3752CANGL
+9630GOELETTE JEAN LOUIS THIERRY 11972 4199407199307 9597 8728.1352CSANS
+1127EXUPERY SIMONE 21972 5199602199502 9894 8921.4152CTECHN
+6944LIBERTE CORINNE MARIE 21972 41993071993051267210589.5253CSANS
+3755ARNAUD MARIE PIERRE 21972 6199307199207 9597 8729.3052MALLEM
+2437CLAUSONNES MARTINE 2197212199411199311 9126 8457.1152MSANS
+2896LUNA NADINE MARIE 21972 8199308199208 9597 8728.3252CTECHN
+1758KARU LILIANE 21972 3199207199107 9597 8729.7452CINFOR
+5392FER RADHIA 21972 3199210199106 8359 7952.9152.SANS
+9857EGLISE CLAIRE 21972 7199402199302 9126 8457.1152CSANS
+6707INKERMANN GERARD 119731219951119941110668 9426.5952CSANS
+4728ESTANISLAO ERIC 11973 1199608199508 7803 7680.7752CSANS
+6566GOULAIN BRIGITTE 21973121994121993121206910240.7753CESP
+0727PUY THERESE 2197311199610199510 8656 8145.6552LTECHN
+0980ARGENTIERE GILLES 1197311199406199306 9126 8456.1552CANGL
+4718DOMOY MICHEL 11973 219940819930811389 9852.0052CSANS
+0216CALAIS FREDERIQUE 21973 219930919920911389 9850.6152MVENTE
+2997FOIRAIL COLAS JEAN LOUP 11973 1199604199504 8656 8145.4852CINFOR
+5877EPI LUC 11973 5199308199208 9597 8729.3052CANGL
+0287CARTIER JEAN LUC 11973 7199511199411 9894 8923.3852CTECHN
+7891MOUSTRAN PAUL 11973 2199303199203 9597 8729.8452CSANS
+3815ELYSEE LOUIS 1197311199304199204 9597 8728.6652CSANS
+0959REPUBLIQUE ERICK 11973 619930719951111389 9852.2752CESP
+2490GLENANS PHILIPPE 11973 41995101994101206910241.8452CSANS
+6935PAYRAC JEAN JACQUES 11973 9199310199210 9597 8729.2252MSANS
+8784JACQUET FREDERICK 11973 319930919920911389 9852.2452CINFOR
+7986COUCHANT YOLANDE 2197312199608199508 7803 7679.4652CALLEM
+1549DRYADES PIERRE G 11973 9199408199307 9126 8456.7052CSANS
+0968OPIO SYLVIE 21974 6199606199506 8656 8145.3852CSANS
+0353DELATTRE FREDERIQUE 21974 9199603199503 8656 8145.8052CINFOR
+1122ACACIAS SERGE 11974101995101994101206910240.9452CTECHN
+9008MANOIR DANIEL R 11974 9199606199506 9894 8921.4052CSANS
+3650AIRES FERNANDO 11974 7199407199307 8656 8146.2552CSANS
+7799TOUR JEAN PIERRE 11974 3199508199408 9126 8456.3752CSANS
+4614VILLERS CHRISTIAN 11974 119951119941110668 9427.4752CSANS
+6384SEURAT JEAN JACQUES 11974 1199504199404 9126 8457.4952CSANS
+0504MEYRIN MICHEL 11974 8199607199507 7803 7679.3752CTECHN
+3076TAMARIS THIERRY 1197411199405199305 9126 8457.7452CALLEM
+5273CAYES PHILIPPE NICOLAS 11974 2199308199208 9597 8729.3452CALLEM
+9558BATOUCH PAUL 11974 6199606199506 8656 8145.2452CSANS
+6082PEDRO GERARD 11974 6199607199507 7803 7680.3252CSANS
+4712CIDEX NICOLE 21974 9199607199507 7803 7680.0052CALLEM
+7982VILAR MICHELINE 21974 9199604199504 9894 8921.4052CALLEM
+0377RIBE MARTINE 2197411199609199509 8656 8146.6152CVENTE
+1910AUBE DOMINIQUE 21974 3199610199510 8656 8146.0152CANGL
+1632COURTAU MARIE JOSEE 21974 8199609199509 7803 7679.6352CSANS
+0616HIIS BEATRICE 21974 919930919920911389 9850.9852MSANS
+5825TIREUSES MARIE FRANCOISE 2197512199609199509 7803 7680.7152CANGL
+8598SAND CHRISTIANE 2197511199607199507 9894 8922.0552CSANS
+9425VISA GILLES 11975 319951119950211389 9851.7452CALLEM
+8440EYGAUX LAURENT 11975 6199603199507 9894 8922.1652CSANS
+3240BARBUSSE JEAN PIERRE 11975 6199510199410 8656 8146.2552CANGL
+7022LOUPS DOMINIQUE 11975 3199605199505 8656 8146.7752CSANS
+3687SULTZER MARYLINE 21975 8199607199507 7803 7681.1752CTECHN
+3649TROIS ANGELICA 2197511199608199508 8656 8147.0652CSANS
+8797BELHAITRE RENE 11975 4199409199309 9126 8455.7652CSANS
+3401SOURCE PAUL 11975 2199612199512 9894 8921.4152CANGL
+0533JOUFFROY LUCIENNE 2197511199608199508 9894 8922.0052CTECHN
+4412CAPEAU MONIQUE 21976 1199607199507 7803 7680.6252CINFOR
+9518COCULOT HELENE 21976 8199508199408 7803 7681.2952CSANS
+1220VALLOIS ALAIN 11976 7199607199507 7803 7680.1852CANGL
+7054DANGALYS MARTINE 2197712199607199507 7803 7680.7752CSANS
+5515MARNE GHISLAINE 21977 6199607199507 7803 7680.0552CSANS
+2958CLAIR GISELE 21977 8199608199508 7803 7680.6352CANGL
+5379CONCHES DOMINIQUE 21977 6199607199507 7803 7679.4652CANGL
+9250DECHAMBRE MICHEL 11977 8199607199507 7803 7679.4552CSANS
+7969SALEON ALAIN 11977 8199607199507 7803 7679.3652CANGL
+0131EOLE ROXANE RENEE 2197710199607199507 7803 7679.2852CANGL
+9710OUEST HENRIE MARCEL 11979 2199607199507 7464 7525.1452CINFOR
+0487PRIEUR JEAN FRANCOIS 11956 81983041982041684813423.3153MSANS
+7696BELVEDERE XAVIER 11962 81982071995091450311830.6753MINFOR
+2063LIOURA PASCAL JEAN 11964 41986111995082256617145.7853MANGL
+7927TRAVERSIERE JEAN PIERRE 11965111986081995091450311832.1553MCOMPT
+2549VASCO DANIEL 11968 51990091995111450311832.1453MANGL
+2675RESIDENCE JEAN MARC 11969 21995111994111450311831.3453CANGL
+8649VIGIER OLIVIER 11969 8199612199512 4638 5819.0153MSANS
+7524BEDOK STEPHANE AUGUSTE 11969 51995121994121450311832.5653CSANS
+3056FRANCE MARION 21971 61996091995091450311831.7553CSANS
+9905CORNICHE DANIEL 11972 31996091995091450311832.2057CSANS
+4894PORTE RONAN JEAN PIERRE 11972121993091992091450311831.9353CSANS
+6471SUCCURSALE LAURE MARIE 21972 71996091995091450311832.1854CALLEM
+8530TOURS ANNE YVONNE 21973 31995121994121450311832.3854CSANS
+4004MORTON JEAN CLAUDE 11965 81992031991031965617846.6854CANGL
+4099MONTELIER DENIS 11971 519940819930811389 9851.1257MSANS
+9423CHATEAU CARL 11956 51980091992121471512024.3958MSANS
+2026BERT CLAUDE 21957 51979031986021497412181.1158MALLEM
+0951MERAY MARIE HELENE 21964 61990051989051249910472.7058MTECHN
+5282PINEL LILIANE 21948 71977031992011471512024.4859MINFOR
+5545DOM ANNIE 21960121982091995061638413150.2959DSANS
+4161BOIS ANNICK 21960 81983021982021326611016.2459MSANS
+1624LORRAINE CHRISTIAN 11961 41983081991012781920674.8359MSANS
+0982VEYANS MICHEL 11961 21982091991092529918968.7059MINFOR
+0123LOCARNO ANNE MARIE 2197411199402199302 8359 7953.1159MSANS
+7957AVAUX PHILIPPE 11942 51963041991072273717261.4960CCOMPT
+0611SAUVAN JEAN FRANCOIS 11946 71980111994011471512025.0260MTECHN
+8634MONTGAZIN ANDRE 11950121974061991012137316331.7560MALLEM
+3872AZUR JEAN CLAUDE 11951101970101990101932314935.1760MMICRO
+7401PARC PIERRE 11952 91989011994011288210745.9360MSANS
+8111STADE BERNARD 11953 21983021984051249910473.4960MANGL
+8795MOTU JEAN LUC 11959 81979101994011578112722.8260MSANS
+2118BAUDETS MARIE CHRISTINE 21961 31983011982011608112915.3560MTECHN
+8422WIEHN BRUNO 11963121988061992021249910472.6660MANGL
+3871SOLE MARTINE 219661019910919950510876 9503.4760MINFOR
+6169CUOQ CORINNE BRIGITTE 21967 21989121995071249910472.2560MANGL
+2571BARAT MARTINE 21968 81994061996012303517377.3560CSANS
+4114HOURTINS DOMINIQUE 11977 1199608199508 7803 7680.5960CANGL
+1079GUEN HELENE 219391219820419810411131 9657.7561MSANS
+0830EGLY DANIELLE 21950 1199406199306 9126 8457.4261MSANS
+1903GENEVIEVE HARDY 11953 91978111989112256617146.1661MINFOR
+9550INGERSHEIM PHILIPPE 11961 61982041987041608112917.1961MANGL
+3284BELLETRUD JEAN 11950 61977101993121369311287.2363MALLEM
+2911SOUBISE IVAN 11958 71982031992112529918967.7663MTECHN
+5851CREACH FRANCOIS 21963 9199608199508 7803 7680.0863MSANS
+6333DUPARCHY LILIANE 21973 3199406199306 9894 8921.6763CSANS
+1729CURAT ROBERT 11935 91958051994123417325098.2464MSANS
+7436CHALLONNIERE VALERIE 21941 41962041989012137316332.3664MSANS
+6301BLUM CHANTAL 21943 71969071991051932314935.2664MSANS
+7026SARRETTE MICHEL 11946 21991011990013200223583.2364.ANGL
+0646MARIE BRIGITTE 21950111971011992061834514275.6164DMANAG
+1101ENFANTS ANTOINETTE 21955 61981011995011527112373.9164MANGL
+8770RACHERET LAURENCE 21956 71978061993051608112916.8864MANGL
+4751LIONS DAVID 11958 91980071993092090215981.1464MANGL
+9291SOULEYAS DOMINIQUE 11958 11980011992101911114779.7764MANGL
+2505LUCHON JACQUELINE 21958 21989011993041249910473.4964MSANS
+8607VOLVESTRE ELISABETH 21958 11991111990111249910472.9764SALLEM
+1586PESSAC JEAN YVES 11961 21989051988051949415050.2864CSANS
+5341RIOU ROBERT 11962 61985091993011949415052.4365MCOMPT
+8946MOULINET ANNE 21962 81983041982041608112915.6064MSANS
+5619MARCQ MONIQUE 21963101995101994101206910241.3064MSANS
+8989JUZAN CLAUDE 11965101990091994092401918075.4464CANGL
+0763PERAULT CHANTAL 21965 21986041985041527112373.5364MSANS
+1526JEREZ NICOLE 21967 519911219920111389 9851.6164.ALLEM
+2211KELLERMANN FREDERIQUE 21967 91991111990111202810241.9764MSANS
+4302KERCADORET DOMINIQUE 11971 81994071993071206910240.2364CSANS
+1236PALETTE ROSE MAY 21971 91994031993031267210588.9264CSANS
+8225SEROT MARTINE 21972 11992061992031330611017.7065CALLEM
+5326DRAGUIGNAN FRANCOISE 21972 519930619920611389 9852.0564MTECHN
+5970DANUBE PHILIPPE 11974 11994071993071206910240.4965CSANS
+6556BROUILLET ANTOINE 11937 11986101986012687820055.0565.SANS
+1186AOUT JEAN JACQUES 11937 21987011986012431618272.0165.COMPT
+7999PIERREFONDS STEPHANE 11938 31964051994011663613342.7665MINFOR
+1312GRENOUILLERE PHILIPPE 11939 71964101987016400345693.2465MALLEM
+0460MARRONNIERS GERARD 11942 41961061994113587826261.7965MINFOR
+4154LOUIS PHILIPPE 11942121965101993012739020403.3065MVENTE
+3603MUETTE GEORGES CHARLES 11942 41968071992013844228044.8465MANGL
+8992COUDREE PASCAL 11943 91968031988072273717260.3265MSANS
+9382CONSTANCE MADELEINE 21944 519830619860211131 9657.1265VSANS
+0255MARTINIQUE GERARD 11946 31994121993123882428316.7365MSANS
+8195EXELMANS BERNARD 11946 91984051984111215710278.1665MVENTE
+3879GOUT JACQUES 11947 51968121992122470018540.5765MSANS
+9678HETRES VIANNEY 11947 91968091994094121428006.1765MALLEM
+6027STRASBOURD JEAN CLAUDE 11951 21972101987052470018540.1565MALLEM
+7805BESSIERES JEAN 11952 51973031988112334017609.4265MSANS
+7152REUILLY ANNE 21953 91974071993121471512024.5665MINFOR
+4995BERCHET FRANCOISE 2195412199306199206 8656 8145.6065MMANAG
+1933ERMITAGE JEAN CLAUDE 11955 11991031994021249910471.6165MSANS
+0728VOISEMBERT ISABELLE 21955 61976011991011471512024.2165MSANS
+0718CERNAY MARC JEAN MARIE 11959 11988011987013669126843.7365.SANS
+2726ORDENER MARIE FRANCOISE 21959 21980071991101369311287.9765MSANS
+0225PLELO PHILIPPE 119591119800319790311389 9851.3365MSANS
+0630GOBELINS BERNARD 11959 71981031991012781920674.6165MTECHN
+1138ISARDS ELIANE 21960 21988101990042581519316.5865.SANS
+8716TOURELLES JOSETTE 21961 81983041989112090215980.4365DANGL
+3837CONTAMINES ANNIE 21961 81984121992061527112373.2265MANGL
+0223FOUSSEAU DOMINIQUE 21961 21996071995082005015476.6165DINFOR
+8553OUSTALADO PIERRE 11962 61984011993121326611017.7265MSANS
+1723HIGH MURIEL 21962 71983011982011608112917.1865MSANS
+2779PLAT MICHEL 11963121984051993102401918074.6765CANGL
+7160BOILEAU DENIS 11964111985031990111949415051.9565MANGL
+1054MARINET CRISTOFORO 11964101985091993092401918076.4465MALLEM
+0034ANGELY JEAN PIERRE 11965 31988081991121249910473.3265MINFOR
+0684DIEUDONNE CORINNE 21965101988121987121249910473.7465MSANS
+4943SICARD DOMINIQUE 21966 919931019921010363 9233.2765MINFOR
+8211CARLE PATRICK 11966 31994011993011621213033.5665MALLEM
+1276GREFFIER SERGE 11967 119930619940110024 8999.1365CSANS
+5313BIARRITZ CHRISTINE 21967 91990111991012602319433.9065.ALLEM
+5161DUBOC NICOLE 219671219880119870111302 9774.4865MANGL
+1515SABLEAUX REGINE 21969 71993011992011839014313.2665MSANS
+7518LANORVILLE JEAN DAVID ALBERT 119701219960519950511389 9851.7065CSANS
+6488CATALAGNE PHILIPPE 11970 71996101995101706413577.4365CSANS
+8039LAURENS PATRICIA 21971111993031995011557012567.4165.SANS
+7894PEPINIERE MARIE ANNE 21971 41991111991111723313653.7565MSANS
+9988MEN JACQUES 11972 71994061993061420611638.1665CANGL
+0742TOUTES JEAN FRANCOIS 11972 71993031992031420611637.6665CANGL
+3618FREDISANE BRUNO 119751019960619950611389 9852.5465CANGL
+7403AUGUSTINES ERICK 11937 61988031987024330331419.2172.ALLEM
+7418PATRICIA JEAN CLAUDE 11944 71990071993011369311286.2072.SANS
+9902CORSE MARIE JOSE 21958 41988101983101326611018.1872.INFOR
+1972FLANDRIN CATHERINE 21963121988101983101288210744.3772.SANS
+6223BEAR MICHELINE 21963 51989061992031450311831.6972.SANS
+1899CHEVILLY ISABELLE 21963 71989011992061450311832.7272MTECHN
+1131BLAISE MIHOKO 21963101990011991061450311831.0372.SANS
+7170AIGUEBONNE MARIE 21971 11995101994101206910241.9972CSANS
+0384LAMBESC ALBERT 11973 119901019920110024 8999.7672.TECHN
+4786VIGEN EVELYNE 21939121962121990102273717261.1873MINFOR
+5969COTE MIDORI 21946121994101993102334017610.5973MSANS
+6378MYOSOTIS YVES 11949 31992011994113844228045.8873.SANS
+3664MAREUIL NADINE 21966 21989061989111288210744.8673.SANS
+8858EANOLIAS JACQUELINE 21949 51977071991121911114778.7075CSANS
+1090LORETTE YVES ANDRE 11950 61971081995013587826261.5875MSANS
+3800KOUTIO ALAIN 11961 91982121989052090215980.3975MINFOR
+5591PALALDA MARC 11965 81995061994061267210590.3275MSANS
diff --git a/storage/connect/mysql-test/connect/std_data/employee.dat b/storage/connect/mysql-test/connect/std_data/employee.dat new file mode 100644 index 00000000000..c571667f9b3 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/employee.dat @@ -0,0 +1,27 @@ +74200 BANCROFT 2 SALESMAN 70012 0318 24888 9600.00 +02345 SMITH 1 ENGINEER 31416 2452 11111 9000.00 +78943 MERCHANT 1 SALESMAN 70012 0318 24888 8700.00 +07654 FUNNIGUY 1 ADMINISTRATOR 40567 0319 33333 8500.00 +45678 BUGHAPPY 1 PROGRAMMER 40567 0319 12345 8500.00 +34567 BIGHEAD 1 SCIENTIST 31416 2452 11111 8000.00 +77777 SHRINKY 2 ADMINISTRATOR 70012 0318 27845 7500.00 +74234 WALTER 1 ENGINEER 70012 0318 24888 7400.00 +56789 FODDERMAN 1 SALESMAN 40567 0319 12345 7000.00 +73452 TONGHO 1 ENGINEER 70012 0318 24888 6800.00 +22222 SHORTSIGHT 2 SECRETARY 87777 0021 5500.00 +55555 MESSIFUL 2 SECRETARY 40567 0319 12345 5000.50 +27845 HONEY 2 SECRETARY 70012 0318 24888 4900.00 +98765 GOOSEPEN 1 ADMINISTRATOR 07654 0319 33333 4700.00 +11111 CHERRY 2 SECRETARY 31416 2452 4500.00 +33333 MONAPENNY 2 SECRETARY 07654 0319 3800.00 +12345 KITTY 2 TYPIST 40567 0319 3000.45 +24888 PLUMHEAD 2 TYPIST 27845 0318 2800.00 +87777 STRONG 1 DIRECTOR 0021 22222 23000.00 +76543 BULLOZER 1 SALESMAN 40567 0319 12345 14800.00 +70012 WERTHER 1 DIRECTOR 87777 0318 27845 14500.00 +40567 QUINN 1 DIRECTOR 87777 0319 55555 14000.00 +31416 ORELLY 1 ENGINEER 87777 2452 11111 13400.00 +36666 BIGHORN 1 SCIENTIST 31416 2452 11111 11000.00 +00137 BROWNY 1 ENGINEER 40567 0319 12345 10500.00 +73111 WHEELFOR 1 SALESMAN 70012 0318 24888 10030.00 +00023 MARTIN 1 ENGINEER 40567 0319 12345 10000.00 diff --git a/storage/connect/mysql-test/connect/std_data/funny.txt b/storage/connect/mysql-test/connect/std_data/funny.txt new file mode 100644 index 00000000000..e69cd7db311 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/funny.txt @@ -0,0 +1,3 @@ +12345,'BERTRAND',#200;5009.13 +56, 'POIROT-DELMOTTE' ,#4256 ;18009 +345 ,'TRUCMUCHE' , #67; 19000.25 diff --git a/storage/connect/mysql-test/connect/std_data/funny2.txt b/storage/connect/mysql-test/connect/std_data/funny2.txt new file mode 100644 index 00000000000..f9080e74d9a --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/funny2.txt @@ -0,0 +1,3 @@ +12345,'BERTRAND',#200;5009.13 +56, 'POIROT-DELMOTTE' ,# ;18009 +345 ,'' , #67; 19000.25 diff --git a/storage/connect/mysql-test/connect/std_data/latin1.xml b/storage/connect/mysql-test/connect/std_data/latin1.xml new file mode 100644 index 00000000000..c6f1f2c8041 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/latin1.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<a><b><c>ÁÂÃÄÅÆÇ</c></b></a> diff --git a/storage/connect/mysql-test/connect/std_data/nocs.xml b/storage/connect/mysql-test/connect/std_data/nocs.xml new file mode 100644 index 00000000000..b2f110858f4 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/nocs.xml @@ -0,0 +1,2 @@ +<?xml version="1.0"?> +<a><b><c>ÁÂÃÄÅÆÇ</c></b></a> diff --git a/storage/connect/mysql-test/connect/std_data/people.csv b/storage/connect/mysql-test/connect/std_data/people.csv new file mode 100644 index 00000000000..64567aec9d0 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/people.csv @@ -0,0 +1,3 @@ +Name;birth;children +"Archibald";17/05/01;3 +"Nabucho";12/08/03;2 diff --git a/storage/connect/mysql-test/connect/std_data/sexe.csv b/storage/connect/mysql-test/connect/std_data/sexe.csv new file mode 100644 index 00000000000..37d63169133 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/sexe.csv @@ -0,0 +1,3 @@ +0;Inconnu
+1;Masculin
+2;Feminin
diff --git a/storage/connect/mysql-test/connect/std_data/sitmat.csv b/storage/connect/mysql-test/connect/std_data/sitmat.csv new file mode 100644 index 00000000000..e93f121a839 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/sitmat.csv @@ -0,0 +1,7 @@ +.;Inconnu
+C;Celibataire
+D;Divorce
+L;Union libre
+M;Marie
+S;Separe
+V;Veuf
diff --git a/storage/connect/mysql-test/connect/std_data/test.sqlite3 b/storage/connect/mysql-test/connect/std_data/test.sqlite3 Binary files differnew file mode 100644 index 00000000000..97cd5d38029 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/test.sqlite3 diff --git a/storage/connect/mysql-test/connect/std_data/xsample.xml b/storage/connect/mysql-test/connect/std_data/xsample.xml new file mode 100644 index 00000000000..20c67fe123b --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/xsample.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<BIBLIO SUBJECT="XML"> + <BOOK ISBN="9782212090819" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>Jean-Christophe</FIRSTNAME> + <LASTNAME>Bernadac</LASTNAME> + </AUTHOR> + <AUTHOR> + <FIRSTNAME>François</FIRSTNAME> + <LASTNAME>Knab</LASTNAME> + </AUTHOR> + <TITLE>Construire une application XML</TITLE> + <PUBLISHER> + <NAME>Eyrolles</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> + <BOOK ISBN="9782840825685" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>William J.</FIRSTNAME> + <LASTNAME>Pardi</LASTNAME> + </AUTHOR> + <TRANSLATOR PREFIX="adapté de l'anglais par"> + <FIRSTNAME>James</FIRSTNAME> + <LASTNAME>Guerin</LASTNAME> + </TRANSLATOR> + <TITLE>XML en Action</TITLE> + <PUBLISHER> + <NAME>Microsoft Press</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> +</BIBLIO> diff --git a/storage/connect/mysql-test/connect/suite.opt b/storage/connect/mysql-test/connect/suite.opt new file mode 100644 index 00000000000..a67571f52ae --- /dev/null +++ b/storage/connect/mysql-test/connect/suite.opt @@ -0,0 +1 @@ +--plugin-load-add=$HA_CONNECT_SO --plugin-connect=ON diff --git a/storage/connect/mysql-test/connect/suite.pm b/storage/connect/mysql-test/connect/suite.pm new file mode 100644 index 00000000000..8ae5ca7d886 --- /dev/null +++ b/storage/connect/mysql-test/connect/suite.pm @@ -0,0 +1,9 @@ +package My::Suite::Connect; + +@ISA = qw(My::Suite); + +return "No CONNECT engine" unless $ENV{HA_CONNECT_SO} or + $::mysqld_variables{'connect'} eq "ON"; + +bless { }; + diff --git a/storage/connect/mysql-test/connect/t/bin.test b/storage/connect/mysql-test/connect/t/bin.test new file mode 100644 index 00000000000..0266fd3e357 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/bin.test @@ -0,0 +1,81 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=BIN; +let $FILE_EXT=BIN; +--source grant.inc + +--copy_file $MTR_SUITE_DIR/std_data/Testbal.dat $MYSQLD_DATADIR/test/Testbal.dat + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 +( + ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=BIN FILE_NAME='nonexistent.txt'; +--replace_regex /on .*test.nonexistent.txt/on DATADIR\/test\/nonexistent.txt/ +# TODO: check why this is needed for Windows +--replace_result Open(rt) Open(rb) +SELECT * FROM t1; +DROP TABLE t1; + +SET time_zone='+00:00'; +CREATE TABLE t1 +( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) not null, + birth DATE NOT NULL, + id CHAR(5) NOT NULL FIELD_FORMAT='S', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', + dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN BLOCK_SIZE=5 FILE_NAME='Testbal.dat'; +SELECT * FROM t1; + +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (55555,'RONALD','1980-02-26','3333',4444.44,555); +INSERT INTO t1 VALUES (5555,'RONALD','1980-02-26','3333',4444.44,555); +SELECT * FROM t1; + +DROP TABLE t1; + +--echo # +--echo # Testing READONLY tables +--echo # +CREATE TABLE t1 +( + fig INT(4) NOT NULL FIELD_FORMAT='C', + name CHAR(10) not null, + birth DATE NOT NULL, + id CHAR(5) NOT NULL FIELD_FORMAT='S', + salary DOUBLE(9,2) NOT NULL DEFAULT 0.00 FIELD_FORMAT='F', + dept INT(4) NOT NULL FIELD_FORMAT='S' +) ENGINE=CONNECT TABLE_TYPE=BIN READONLY=Yes FILE_NAME='Testbal.dat'; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +ALTER TABLE t1 READONLY=NO; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +SELECT * FROM t1; +ALTER TABLE t1 READONLY=YES; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (7777,'BILL','1973-06-30',4444,5555.555,777); +DROP TABLE t1; + + +--echo # +--echo # Testing that the underlying file is created +--echo # +CREATE TABLE t1 +( + c CHAR(4) NOT NULL FIELD_FORMAT='C' +) ENGINE=CONNECT TABLE_TYPE=BIN FILE_NAME='bin2.dat'; +INSERT INTO t1 VALUES (10),(20),(300),(4000); +SELECT * FROM t1; +DROP TABLE t1; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/Testbal.dat +--remove_file $MYSQLD_DATADIR/test/bin2.dat diff --git a/storage/connect/mysql-test/connect/t/csv.test b/storage/connect/mysql-test/connect/t/csv.test new file mode 100644 index 00000000000..6578ba83a9a --- /dev/null +++ b/storage/connect/mysql-test/connect/t/csv.test @@ -0,0 +1,189 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=CSV; +let $FILE_EXT=CSV; +--source grant.inc + +--copy_file $MTR_SUITE_DIR/std_data/people.csv $MYSQLD_DATADIR/test/people.csv + +SET NAMES utf8; + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 +( + ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=CSV FILE_NAME='nonexistent.txt'; +--replace_regex /on .*test.nonexistent.txt/on DATADIR\/test\/nonexistent.txt/ +# TODO: check why this is needed for Windows +--replace_result Open(rt) Open(rb) +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Testing examples from the manual +--echo # +CREATE TABLE t1 +( + name CHAR(12) NOT NULL, + birth DATE NOT NULL DATE_FORMAT='DD/MM/YY', + children SMALLINT(2) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='people.csv' + HEADER=1 SEP_CHAR=';' QUOTED=1; +SELECT * FROM t1; +INSERT INTO t1 VALUES ('RONALD','1980-02-26',4); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/people.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/people.csv'),'\r\n','\n'); + +--echo # +--echo # Testing READONLY tables +--echo # +CREATE TABLE t1 +( + name CHAR(12) NOT NULL, + birth DATE NOT NULL DATE_FORMAT='DD/MM/YY', + children SMALLINT(2) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='people.csv' + HEADER=1 SEP_CHAR=';' QUOTED=1 READONLY=yes; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +--error ER_GET_ERRMSG +UPDATE t1 SET children=6 WHERE name='BILL'; +--error ER_GET_ERRMSG +DELETE FROM t1 WHERE name='BILL'; +--error ER_GET_ERRMSG +TRUNCATE TABLE t1; +SELECT * FROM t1; +ALTER TABLE t1 READONLY=no; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +SELECT * FROM t1; +ALTER TABLE t1 READONLY=1; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES ('BILL','1973-06-30',5); +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing that the underlying file is created +--echo # +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL, + c2 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='tmp.csv' + HEADER=1 SEP_CHAR=',' QUOTED=1; +INSERT INTO t1 VALUES (10,10),(20,20),(300,300),(4000,4000), ('a b','c d'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/tmp.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/tmp.csv'),'\r\n','\n'); + +--echo # +--echo # Creating a CSV table from a MyISAM table +--echo # +CREATE TABLE t1 (a VARCHAR(10) NOT NULL, b INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('test1',1), ('test2',2); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t2.csv' + AS SELECT * FROM t1; +SELECT * FROM t2; +DROP TABLE t2; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t2.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t2.csv'),'\r\n','\n'); +--remove_file $MYSQLD_DATADIR/test/t2.csv + +--echo # +--echo # Testing international data +--echo # +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=utf8; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=utf8 DATA_CHARSET=latin1; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv'; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=latin1; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + +CREATE TABLE t1 +( + c1 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv' + CHARSET=latin1 DATA_CHARSET=utf8; +INSERT INTO t1 VALUES ('á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + +CREATE TABLE t1 +( + c1 CHAR(12) CHARACTER SET latin1 NOT NULL, + c2 CHAR(12) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='t1.csv'; +INSERT INTO t1 VALUES ('á','á'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.csv +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT HEX(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.csv'),'\r\n','\n')); +--remove_file $MYSQLD_DATADIR/test/t1.csv + + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/people.csv +--remove_file $MYSQLD_DATADIR/test/tmp.csv diff --git a/storage/connect/mysql-test/connect/t/dbf.test b/storage/connect/mysql-test/connect/t/dbf.test new file mode 100644 index 00000000000..fb0bc21f52d --- /dev/null +++ b/storage/connect/mysql-test/connect/t/dbf.test @@ -0,0 +1,513 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=DBF; +let $FILE_EXT=DBF; +--source grant.inc + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +SHOW CREATE TABLE t1; +--replace_regex /on .*test.t1.dbf/on DATADIR\/test\/t1.dbf/ +SELECT * FROM t1; +DROP TABLE t1; + +--replace_regex /Cannot open .*test.t1.dbf/Cannot open DATADIR\/test\/t1.dbf/ +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--replace_regex /Cannot open .*test.t1.dbf/Cannot open DATADIR\/test\/t1.dbf/ +SHOW WARNINGS; + + +DELIMITER //; +CREATE PROCEDURE test.dbf_field(in fieldno INT, in content BLOB) DETERMINISTIC +BEGIN + SELECT '---'; + SELECT fieldno AS `FieldN`; + SELECT TRIM(TRAILING 0x00 FROM LEFT(content, 10)) AS `Name`; + SELECT SUBSTRING(content, 12, 1) AS `Type`; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,13,4))),16,10) AS `Offset`; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,17,1))),16,10) AS `Length`; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,18,1))),16,10) AS `Dec`; + SELECT HEX(REVERSE(SUBSTRING(content,19,1))) AS `Flags`; +-- SELECT CONV(HEX(REVERSE(SUBSTRING(content,20,4))),16,10) AS `Next`; +-- SELECT CONV(HEX(REVERSE(SUBSTRING(content,24,4))),16,10) AS `Step`; +END// + +CREATE PROCEDURE test.dbf_header(in fname VARCHAR(1024)) DETERMINISTIC +BEGIN + DECLARE content BLOB; + DECLARE offset INT; + DECLARE fieldno INT; + SELECT '--------'; + SELECT LOAD_FILE(fname) INTO content; + SELECT LENGTH(content) AS FileSize; + SELECT HEX(LEFT(content, 1)) AS DBF_Version; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,5,4))),16,10) AS NRecords; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,9,2))),16,10) AS FirstRecPos; + SELECT CONV(HEX(REVERSE(SUBSTRING(content,11,2))),16,10) AS RecLength; + SELECT HEX(REVERSE(SUBSTRING(content,29,2))) AS TableFlags; + SELECT HEX(REVERSE(SUBSTRING(content,30,1))) AS CodePageMark; + SET offset=33; + SET fieldno=0; + WHILE SUBSTR(content, offset, 1) <> 0x0D AND offset + 32 < LENGTH(content) DO + CALL dbf_field(fieldno, SUBSTRING(content, offset, 32)); + SET offset=offset + 32; + SET fieldno=fieldno + 1; + END WHILE; + SELECT '--------'; +END// +DELIMITER ;// + + +--echo # +--echo # Testing READONLY tables +--echo # +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (10),(20); +SELECT * FROM t1; +ALTER TABLE t1 READONLY=Yes; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (30); +--error ER_GET_ERRMSG +UPDATE t1 SET a=30 WHERE a=10; +--error ER_GET_ERRMSG +DELETE FROM t1 WHERE a=10; +--error ER_GET_ERRMSG +TRUNCATE TABLE t1; +ALTER TABLE t1 READONLY=NO; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (30); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # This SQL script crashed (dbf01.sql) +--echo # +CREATE TABLE t1 +( + a int(11) NOT NULL, + b char(10) NOT NULL, + c varchar(10) NOT NULL +) ENGINE=CONNECT table_type=DBF file_name='t1.dbf'; +INSERT INTO t1 VALUES (1,'1','1'); +INSERT INTO t1 VALUES (2,'2','2'); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing that table options in lower case and mixed case are understood: +--echo # +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT table_type=dbf file_name='t1.dbf'; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf +CREATE TABLE t1 (a CHAR(10) NOT NULL) ENGINE=CONNECT Table_Type=dbf File_Name='t1.dbf'; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('test'); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +# +# TODO: this creates DBF record with length=32, which looks wrong +# +--echo # +--echo # Testing multiple columns +--echo # +CREATE TABLE t1 +( + a INT NOT NULL, + b CHAR(10) NOT NULL, + c VARCHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (1,'1','1'); +INSERT INTO t1 VALUES (2,'2','2'); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing long column name +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a012345678901234567890123456789 INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; + +--echo # +--echo # Testing 2 columns with long names (12) +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a0123456789a INT NOT NULL, + b0123456789b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x11.dbf'; + +--echo # +--echo # Testing 2 columns with long names (11) +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a012345678a INT NOT NULL, + b012345678b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x12.dbf'; + +--echo # +--echo # Testing 2 columns name length 10 (maximum possible length) +--echo # +CREATE TABLE t1 +( + a01234567a INT NOT NULL, + b01234567b INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t02x13.dbf'; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1,2); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t02x13.dbf + + +--echo # +--echo # Testing BIGINT +--echo # +CREATE TABLE t1 +( + a bigint NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (0x7FFFFFFFFFFFFFFF); +INSERT INTO t1 VALUES (-0x8000000000000000); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing TINYINT +--echo # +CREATE TABLE t1 +( + a TINYINT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing SMALLINT +--echo # +CREATE TABLE t1 +( + a SMALLINT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (0x7FFF); +INSERT INTO t1 VALUES (-0x8000); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing VARCHAR +--echo # +CREATE TABLE t1 +( + a VARCHAR(255) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (REPEAT('a',255)); +SELECT LENGTH(a) FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing too long CHAR +--echo # All columns longer than 255 bytes should be rejected +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a CHAR(86) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR + + +--echo # +--echo # Testing too long VARCHAR +--echo # All columns longer than 255 bytes should be rejected +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a VARCHAR(256) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a VARCHAR(86) CHARACTER SET utf8 NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a VARCHAR(64000) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; + + +--echo # +--echo # Testing BLOB +--echo # +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a BLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a TINYBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a MEDIUMBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + a LONGBLOB +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; + + +# TODO: utf8 does not work +#--echo # +#--echo # Testing varchar with utf8 +#--echo # +#SET NAMES utf8; +#CREATE TABLE t1 +#( +# a VARCHAR(10) CHARACTER SET utf8 +#) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +#INSERT INTO t1 VALUES (REPEAT(_ucs2 0x00DF,10)); +#SELECT * FROM t1; +#DROP TABLE IF EXISTS t1; +#--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing DATE +--echo # +CREATE TABLE t1 +( + a DATE NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('2001-01-01'); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + + +--echo # +--echo # Testing FLOAT +--echo # +CREATE TABLE t1 +( + a FLOAT(12,4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf +# +# TODO: this return error: +# Got error 122 'Value 123.0000000000 too long for column a of length 12' +# from CONNECT +# +#CREATE TABLE t1 +#( +# a FLOAT NOT NULL +#) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +#--error ER_GET_ERRMSG - why this error? +#INSERT INTO t1 VALUES (123); +#SELECT * FROM t1; +#DROP TABLE IF EXISTS t1; +#--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +# +# TODO: this creates a column of type 'D' (date), which is wrong +# +#--echo # +#--echo # Testing DATETIME +#--echo # +#CREATE TABLE t1 +#( +# a DATETIME NOT NULL +#) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +#INSERT INTO t1 VALUES ('2013-02-01'); +#SELECT * FROM t1; +#DROP TABLE t1; +#--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +# +# TODO: this creates a column of type 'D' (date), which is wrong +# +#--echo # +#--echo # Testing TIMESTAMP +#--echo # +#CREATE TABLE t1 +#( +# a TIMESTAMP +#) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +#INSERT INTO t1 VALUES ('2013-02-01'); +#SELECT * FROM t1; +#DROP TABLE t1; +#--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing double +--echo # +CREATE TABLE t1 +( + a DOUBLE(20,5) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES (123); +INSERT INTO t1 VALUES (123456789.12345); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE IF EXISTS t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +# TODO: +# Testing with no FILE_NAME specified +# Currently it returns: +# ERROR 1296 (HY000): Got error 174 'Open(a+) error 21 +# on /opt/mariadb-5.5/data/: Is a directory' from CONNECT +#CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=DBF; + +--echo # +--echo # Testing ALTER +--echo # +CREATE TABLE t1 +( + a VARCHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('10'); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +ALTER TABLE t1 MODIFY a VARCHAR(10) NOT NULL; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +ALTER TABLE t1 MODIFY a INT(10) NOT NULL; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results + +# TODO: this does not work on Windows +#ALTER TABLE t1 MODIFY a INT(8) NOT NULL; +#SHOW CREATE TABLE t1; +#--error ER_GET_ERRMSG +#SELECT * FROM t1; +#--vertical_results +#--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +#eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +#--horizontal_results +DROP TABLE IF EXISTS t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + + +--echo # +--echo # Testing NULL +--echo # +# TODO: NULLs should probably change to DEFAULT and produce a warning +CREATE TABLE t1 +( + c1 VARCHAR(10) NOT NULL, + c2 VARCHAR(10) NOT NULL DEFAULT 'def', + i1 INT NOT NULL, + i2 INT NOT NULL DEFAULT 123 +) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='t1.dbf'; +INSERT INTO t1 VALUES ('10','10',10,10); +#INSERT INTO t1 VALUES (NULL,NULL,NULL,NULL); +INSERT INTO t1(c1,i1) VALUES ('20',20); +INSERT INTO t1 VALUES ('30',DEFAULT,30,DEFAULT); +SELECT * FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.dbf +--vertical_results +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval CALL dbf_header('$MYSQLD_DATADIR/test/t1.dbf'); +--horizontal_results +DROP TABLE IF EXISTS t1; +--remove_file $MYSQLD_DATADIR/test/t1.dbf + +DROP PROCEDURE test.dbf_field; +DROP PROCEDURE test.dbf_header; diff --git a/storage/connect/mysql-test/connect/t/dir.test b/storage/connect/mysql-test/connect/t/dir.test new file mode 100644 index 00000000000..1d4ec2cedbf --- /dev/null +++ b/storage/connect/mysql-test/connect/t/dir.test @@ -0,0 +1,125 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + + +--echo # +--echo # Testing FILE privilege +--echo # +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +--connect(user,localhost,user,,) +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 ( + path VARCHAR(256) NOT NULL flag=1, + fname VARCHAR(256) NOT NULL, + ftype CHAR(4) NOT NULL, + size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.*'; +--connection default +SELECT user(); +CREATE TABLE t1 ( + path VARCHAR(256) NOT NULL flag=1, + fname VARCHAR(256) NOT NULL, + ftype CHAR(4) NOT NULL, + size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.*'; +# "size>0" to skip directory names on Windows +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT fname, ftype, size FROM t1 WHERE size>0; + +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES ('xxx'); +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1 WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET a='yyy' WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 READONLY=1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; + +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--connection default +SELECT user(); +CREATE VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1 VALUES (2); +--error ER_ACCESS_DENIED_ERROR +UPDATE v1 SET a=123; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1; + +--disconnect user +--connection default +SELECT user(); +DROP VIEW v1; +DROP TABLE t1; +DROP USER user@localhost; +--echo # +--echo # Testing FILE privileges done +--echo # + + +CREATE TABLE t1 ( + path VARCHAR(256) NOT NULL flag=1, + fname VARCHAR(256) NOT NULL, + ftype CHAR(4) NOT NULL, + size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.txt' + OPTION_LIST='subdir=1'; + +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT * FROM t1; + +--copy_file $MTR_SUITE_DIR/std_data/boys.txt $MYSQLD_DATADIR/test/boys.txt +--copy_file $MTR_SUITE_DIR/std_data/boyswin.txt $MYSQLD_DATADIR/test/boyswin.txt + +--mkdir $MYSQLD_DATADIR/test/subdir/ +--copy_file $MYSQLD_DATADIR/test/boys.txt $MYSQLD_DATADIR/test/subdir/boys2.txt +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT fname, ftype, size FROM t1 ORDER BY fname, ftype, size; +ALTER TABLE t1 OPTION_LIST='subdir=0'; +SHOW CREATE TABLE t1; +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT fname, ftype, size FROM t1 ORDER BY fname, ftype, size; + +# TODO: add a better error message +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES ('','','',''); + +DROP TABLE t1; + +# TODO: automatically add columns +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*.txt'; + + +# +# TODO: this fails on Linux +# +#CREATE TABLE t1 t1 ( +# DRIVE CHAR(2), +# PATH VARCHAR(256), +# FNAME VARCHAR(256), +# FTYPE CHAR(4), +# SIZE DOUBLE(12,0) flag=5, +# MODIFIED datetime +#) engine=CONNECT table_type=DIR file_name='*.txt'; +#SELECT * FROM t1; +#DROP TABLE t1; + +--remove_file $MYSQLD_DATADIR/test/subdir/boys2.txt +--rmdir $MYSQLD_DATADIR/test/subdir/ +--remove_file $MYSQLD_DATADIR/test/boys.txt +--remove_file $MYSQLD_DATADIR/test/boyswin.txt diff --git a/storage/connect/mysql-test/connect/t/fix.test b/storage/connect/mysql-test/connect/t/fix.test new file mode 100644 index 00000000000..d2dfeb21352 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/fix.test @@ -0,0 +1,112 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=FIX; +let $FILE_EXT=FIX; +--source grant.inc + +--copy_file $MTR_SUITE_DIR/std_data/dept.dat $MYSQLD_DATADIR/test/dept.dat +--copy_file $MTR_SUITE_DIR/std_data/boys.txt $MYSQLD_DATADIR/test/boys.txt +--copy_file $MTR_SUITE_DIR/std_data/boyswin.txt $MYSQLD_DATADIR/test/boyswin.txt + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 +( + ID INT NOT NULL +) Engine=CONNECT TABLE_TYPE=DOS FILE_NAME='nonexistent.txt'; +--replace_regex /on .*test.nonexistent.txt/on DATADIR\/test\/nonexistent.txt/ +# TODO: check why this is needed for Windows +--replace_result Open(rt) Open(rb) +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Testing READONLY tables +--echo # +CREATE TABLE t1 +( + id INT NOT NULL +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='t1.txt'; +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +ALTER TABLE t1 READONLY=1; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (20); +--error ER_GET_ERRMSG +UPDATE t1 SET id=20 WHERE id=10; +--error ER_GET_ERRMSG +DELETE FROM t1 WHERE id=10; +--error ER_GET_ERRMSG +TRUNCATE TABLE t1; +ALTER TABLE t1 READONLY=0; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (20); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.txt + + +--echo # +--echo # Testing manual examples +--echo # +CREATE TABLE t1 +( + number CHAR(4) not null, + location CHAR(15) NOT NULL flag=5, + director CHAR(5) NOT NULL flag=20, + function CHAR(12) NOT NULL flag=26, + name CHAR(22) NOT NULL flag=38 +) ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='dept.dat'; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + name char(12) not null, + city char(12) not null, + birth date not null date_format='DD/MM/YYYY', + hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' ENDING=1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + name char(12) not null, + city char(12) not null, + birth date not null date_format='DD/MM/YYYY', + hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boys.txt' LRECL=47 ENDING=1; +SELECT * FROM t1; +DROP TABLE t1; + + +CREATE TABLE t1 +( + name char(12) not null, + city char(12) not null, + birth date not null date_format='DD/MM/YYYY', + hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boyswin.txt' ENDING=2; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + name char(12) not null, + city char(12) not null, + birth date not null date_format='DD/MM/YYYY', + hired date not null date_format='DD/MM/YYYY' flag=36 +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='boyswin.txt' LRECL=47 ENDING=2; +SELECT * FROM t1; +DROP TABLE t1; + + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/dept.dat +--remove_file $MYSQLD_DATADIR/test/boys.txt +--remove_file $MYSQLD_DATADIR/test/boyswin.txt diff --git a/storage/connect/mysql-test/connect/t/fmt.test b/storage/connect/mysql-test/connect/t/fmt.test new file mode 100644 index 00000000000..de7f8c06c1b --- /dev/null +++ b/storage/connect/mysql-test/connect/t/fmt.test @@ -0,0 +1,85 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +--copy_file $MTR_SUITE_DIR/std_data/funny.txt $MYSQLD_DATADIR/test/funny.txt +--copy_file $MTR_SUITE_DIR/std_data/funny2.txt $MYSQLD_DATADIR/test/funny2.txt + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 +( + ID INT NOT NULL field_format=' %n%d%n' +) Engine=CONNECT table_type=FMT file_name='nonexistent.txt'; +--replace_regex /on .*test.nonexistent.txt/on DATADIR\/test\/nonexistent.txt/ +# TODO: check why this is needed for Windows +--replace_result Open(rt) Open(rb) +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing update on FMT tables +--echo # +CREATE TABLE t1 +( + id INT NOT NULL field_format=' %n%d%n' +) ENGINE=CONNECT TABLE_TYPE=FMT FILE_NAME='t1.txt'; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (10),(20); +# TODO: +#--error ER_GET_ERRMSG +#UPDATE t1 SET id=20; +#TRUNCATE TABLE t1; +#DELETE FROM t1 WHERE id=10; +#SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.txt + + +--echo # +--echo # Testing manual examples +--echo # +CREATE TABLE t1 +( + ID Integer(5) not null field_format=' %n%d%n', + NAME Char(16) not null field_format=" , '%n%[^']%n'", + DEPNO Integer(4) not null field_format=' , #%n%d%n', + SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny.txt'; +SELECT * FROM t1; +DROP TABLE t1; + +# +# TODO: shoudn't a warning instead of error be returned on bad format? +# +CREATE TABLE t1 +( + ID Integer(5) not null field_format=' %n%d%n', + NAME Char(16) not null field_format=" , '%n%[^']%n'", + DEPNO Integer(4) not null field_format=' , #%n%d%n', + SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny2.txt'; +--error ER_GET_ERRMSG +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + ID Integer(5) not null field_format=' %n%d%n', + NAME Char(16) not null field_format=' , ''%n%[^'']%m', + DEPNO Integer(4) not null field_format=''' , #%n%d%m', + SALARY Double(12,2) not null field_format=' ; %n%f%n' +) Engine=CONNECT table_type=FMT file_name='funny2.txt'; +SELECT * FROM t1; +--error ER_GET_ERRMSG +UPDATE t1 SET SALARY=1234; +# TODO: this query crashes +# UPDATE t1 SET SALARY=1234 WHERE ID=56; +DELETE FROM t1 WHERE ID=56; +SELECT * FROM t1; +DROP TABLE t1; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/funny.txt +--remove_file $MYSQLD_DATADIR/test/funny2.txt diff --git a/storage/connect/mysql-test/connect/t/general.test b/storage/connect/mysql-test/connect/t/general.test new file mode 100644 index 00000000000..66752b32099 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/general.test @@ -0,0 +1,16 @@ +--echo #
+--echo # Testing features not specific to any TABLE_TYPE
+--echo #
+--error ER_UNKNOWN_ERROR
+CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=NON_EXISTING;
+#SHOW CREATE TABLE t1;
+#DROP TABLE t1;
+
+CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=FIX;
+INSERT INTO t1 VALUES (10);
+SELECT * FROM t1;
+#--error ER_GET_ERRMSG
+--error ER_UNKNOWN_ERROR
+ALTER TABLE t1 TABLE_TYPE=NON_EXISTING;
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/t/grant.inc b/storage/connect/mysql-test/connect/t/grant.inc new file mode 100644 index 00000000000..7bb214dc9fd --- /dev/null +++ b/storage/connect/mysql-test/connect/t/grant.inc @@ -0,0 +1,85 @@ +--echo # +--echo # Beginning of grant.inc +--echo # +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +--connect(user,localhost,user,,) +--connection user +SELECT user(); +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT $TABLE_OPTIONS +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +UPDATE t1 SET a=20; +SELECT * FROM t1; +DELETE FROM t1; +SELECT * FROM t1; +INSERT INTO t1 VALUES(10); +TRUNCATE TABLE t1; +SELECT * FROM t1; +# TODO: LOCK, UNLOCK, REFERENCES, INDEX +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +DROP VIEW v1; +DROP TABLE t1; +# Making sure DROP erased the data file +--error 1 +--remove_file $MYSQLD_DATADIR/test/t1.$FILE_EXT +--error ER_ACCESS_DENIED_ERROR +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT $TABLE_OPTIONS FILE_NAME='t1.EXT' +--connection default +SELECT user(); +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT $TABLE_OPTIONS FILE_NAME='t1.EXT' +INSERT INTO t1 VALUES (10); +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES (10); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET a=20; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1; +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 READONLY=1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 FILE_NAME='t2.EXT'; +--error ER_ACCESS_DENIED_ERROR +DROP TABLE t1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--connection default +SELECT user(); +CREATE VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1 VALUES (2); +--error ER_ACCESS_DENIED_ERROR +UPDATE v1 SET a=123; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1; +--connection default +SELECT user(); +DROP VIEW v1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.EXT +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT $TABLE_OPTIONS +INSERT INTO t1 VALUES (10); +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 FILE_NAME='t1.EXT'; +--connection default +DROP TABLE t1; +--disconnect user +DROP USER user@localhost; + +--echo # +--echo # End of grant.inc +--echo # diff --git a/storage/connect/mysql-test/connect/t/index.test b/storage/connect/mysql-test/connect/t/index.test new file mode 100644 index 00000000000..5e913582734 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/index.test @@ -0,0 +1,86 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +--copy_file $MTR_SUITE_DIR/std_data/emp.txt $MYSQLD_DATADIR/test/emp.txt +--copy_file $MTR_SUITE_DIR/std_data/sexe.csv $MYSQLD_DATADIR/test/sexe.csv +--copy_file $MTR_SUITE_DIR/std_data/sitmat.csv $MYSQLD_DATADIR/test/sitmat.csv + +--echo # +--echo # Testing indexing +--echo # +CREATE TABLE t1 +( + matricule INT(4) KEY NOT NULL field_format='Z', + nom VARCHAR(16) NOT NULL, + prenom VARCHAR(20) NOT NULL, + sexe SMALLINT(1) NOT NULL COMMENT 'sexe 1:M 2:F', + aanais INT(4) NOT NULL, + mmnais INT(2) NOT NULL, + ddentree DATE NOT NULL date_format='YYYYMM', + ddnom DATE NOT NULL date_format='YYYYMM', + brut INT(5) NOT NULL, + net DOUBLE(8,2) NOT NULL, + service INT(2) NOT NULL, + sitmat CHAR(1) NOT NULL, + formation CHAR(5) NOT NULL, + INDEX NP(nom,prenom) +) ENGINE=CONNECT TABLE_TYPE=FIX FILE_NAME='emp.txt' ENDING=2; +SELECT * FROM t1 LIMIT 10; +SELECT SUM(brut) from t1; + +--echo # +--echo # Testing file mapping +--echo # +ALTER TABLE t1 MAPPED=yes; +SELECT * FROM t1 LIMIT 10; +SELECT SUM(brut) FROM t1; + +--echo # +--echo # Test the indexes (made when creating the table) +--echo # +SELECT * FROM t1 WHERE matricule = '0091'; +SELECT * FROM t1 WHERE nom = 'FOCH'; +SELECT * FROM t1 WHERE nom = 'FOCH' and prenom = 'DENIS'; + +--echo # +--echo # Testing UPDATE +--echo # +UPDATE t1 SET aanais = aanais + 16; +UPDATE t1 SET ddentree = adddate(ddentree, interval 16 year); +UPDATE t1 SET ddnom = adddate(ddnom, interval 16 year); +SELECT * FROM t1 WHERE nom = 'FOCH'; + +--echo # +--echo # Testing JOIN +--echo # +create table t2 +( + sexe INT(1) KEY, + genre CHAR(8) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='sexe.csv' SEP_CHAR=';' ENDING=2; +SELECT * FROM t2; +SELECT nom, prenom, genre FROM t1 NATURAL JOIN t2 LIMIT 10; + +--echo # +--echo # Another table +--echo # +CREATE TABLE t3 ( + sitmat CHAR(1) KEY, + situation CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='sitmat.csv' SEP_CHAR=';' ENDING=2; +SELECT * FROM t3; +SELECT nom, prenom, genre, situation FROM t1 NATURAL JOIN t2 NATURAL JOIN t3 WHERE nom = 'FOCH'; + +--echo # +--echo # Testing DELETE +--echo # +DELETE FROM t1; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/emp.txt +--remove_file $MYSQLD_DATADIR/test/sexe.csv +--remove_file $MYSQLD_DATADIR/test/sitmat.csv diff --git a/storage/connect/mysql-test/connect/t/ini.test b/storage/connect/mysql-test/connect/t/ini.test new file mode 100644 index 00000000000..e862f3cd672 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/ini.test @@ -0,0 +1,231 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +--echo # +--echo # Checking FILE privileges +--echo # +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +--connect(user,localhost,user,,) +--connection user +SELECT user(); +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI; +INSERT INTO t1 VALUES ('sec1','val1'); +SELECT * FROM t1; +UPDATE t1 SET val='val11'; +SELECT * FROM t1; +DELETE FROM t1; +SELECT * FROM t1; +INSERT INTO t1 VALUES('sec2','val2'); +TRUNCATE TABLE t1; +SELECT * FROM t1; +CREATE VIEW v1 AS SELECT * FROM t1; +SELECT * FROM v1; +DROP VIEW v1; +DROP TABLE t1; +# Making sure DROP erased the data file +--error 1 +--remove_file $MYSQLD_DATADIR/test/t1.ini +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.EXT'; +--connection default +SELECT user(); +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.EXT'; +INSERT INTO t1 VALUES ('sec1','val1'); +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES ('sec2','val2'); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET val='val11'; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1; +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 READONLY=1; +--error ER_ACCESS_DENIED_ERROR +DROP TABLE t1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--connection default +SELECT user(); +CREATE VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1 VALUES ('sec3','val3'); +--error ER_ACCESS_DENIED_ERROR +UPDATE v1 SET val='val11'; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1; +--disconnect user +--connection default +DROP VIEW v1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.EXT +DROP USER user@localhost; + +--echo # +--echo # Checking FILE privileges: done +--echo # + + +--copy_file $MTR_SUITE_DIR/std_data/contact.ini $MYSQLD_DATADIR/test/contact.ini + +--echo # +--echo # Testing errors +--echo # +CREATE TABLE t1 +( + ID INT +) Engine=CONNECT TABLE_TYPE=INI FILE_NAME='nonexistent.txt'; +--replace_regex /on .*test.nonexistent.txt/on DATADIR\/test\/nonexistent.txt/ +# TODO: check why this is needed for Windows +--replace_result Open(rt) Open(rb) +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Testing examples from the manual +--echo # + +CREATE TABLE t1 +( + contact CHAR(16) flag=1, + name CHAR(20), + forename CHAR(32), + hired date date_format='DD/MM/YYYY', + address CHAR(64), + city CHAR(20), + zipcode CHAR(8), + tel CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='contact.ini'; +SELECT contact, name, hired, city, tel FROM t1; + +UPDATE t1 SET forename= 'Harry' where contact='UK1'; +SELECT * FROM t1 WHERE contact='UK1'; +INSERT INTO t1 (contact,forename) VALUES ('UK1','Harrison'); +SELECT * FROM t1 WHERE contact='UK1'; +INSERT INTO t1 (contact,forename) VALUES ('UK2','John'); +SELECT * FROM t1 WHERE contact='UK2'; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/contact.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n'); + +CREATE TABLE t1 +( + section CHAR(16) flag=1, + keyname CHAR(16) flag=2, + value CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='contact.ini' + OPTION_LIST='Layout=Row'; +UPDATE t1 SET value='Paul' WHERE section='UK2' AND keyname='forename'; +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/contact.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/contact.ini'),'\r\n','\n'),'\n\n','\n'); + + +--echo # +--echo # Testing that the underlying file is created +--echo # +CREATE TABLE t1 +( + contact CHAR(12) NOT NULL flag=1, + c2 CHAR(12) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='tmp.ini'; +INSERT INTO t1 VALUES (10,10),(20,20),(300,300),(4000,4000), ('a b','c d'); +SELECT * FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/tmp.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/tmp.ini'),'\r\n','\n'),'\n\n','\n'); + + +--echo # +--echo # Testing bad table +--echo # +CREATE TABLE t1 +( + id INT +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.ini'; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing READONLY tables +--echo # +CREATE TABLE t1 +( + contact CHAR(10) flag=1, + c2 CHAR(60) +) ENGINE=CONNECT TABLE_TYPE=INI FILE_NAME='t1.ini'; +INSERT INTO t1 VALUES ('UK',10),('FR',20),('RU',30); +SELECT * FROM t1; +ALTER TABLE t1 READONLY=1; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES ('US',40); +--error ER_GET_ERRMSG +UPDATE t1 SET c2=20 WHERE c2=10; +--error ER_GET_ERRMSG +DELETE FROM t1 WHERE c2=10; +--error ER_GET_ERRMSG +TRUNCATE TABLE t1; +ALTER TABLE t1 READONLY=0; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('US',40); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1.ini + + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/contact.ini +--remove_file $MYSQLD_DATADIR/test/tmp.ini + + +--echo # +--echo # Bug: TABLE_TYPE=ini does not clear memory between CREATE TABLEs +--echo # +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +INSERT INTO t1 VALUES ('sec1','val1'),('sec2','val2'); +SELECT sec AS s, val AS v FROM t1; +DROP TABLE t1; +CREATE TABLE t1 (sec2 CHAR(10) NOT NULL FLAG=1, val2 CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +INSERT INTO t1 VALUES ('sec1','val11'),('sec2','val22'); +SELECT sec2 AS s, val2 AS v FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n'); +DROP TABLE t1; + +CREATE TABLE t1 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +CREATE TABLE t2 (sec CHAR(10) NOT NULL FLAG=1, val CHAR(10) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=INI; +INSERT INTO t1 VALUES('1sec1','1val1'),('1sec2','1val2'); +INSERT INTO t2 VALUES('2sec1','2val1'),('2sec2','2val2'); +SELECT sec AS s, val AS v FROM t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t1.ini'),'\r\n','\n'),'\n\n','\n'); +SELECT sec AS s, val AS v FROM t2; +--chmod 0777 $MYSQLD_DATADIR/test/t2.ini +--replace_result $MYSQLD_DATADIR DATADIR +--eval SELECT REPLACE(REPLACE(LOAD_FILE('$MYSQLD_DATADIR/test/t2.ini'),'\r\n','\n'),'\n\n','\n'); +DROP TABLE t1, t2; diff --git a/storage/connect/mysql-test/connect/t/mysql.test b/storage/connect/mysql-test/connect/t/mysql.test new file mode 100644 index 00000000000..004a2c21af7 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/mysql.test @@ -0,0 +1,471 @@ +let $PORT= `select @@port`; + +--disable_query_log +--replace_result $PORT PORT +--error 0,ER_UNKNOWN_ERROR +--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1' + AND ENGINE='CONNECT' + AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`) +{ + Skip Need MySQL support; +} +DROP TABLE t1; +--enable_query_log + +--echo # +--echo # Testing FILE privilege +--echo # +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +--connect(user,localhost,user,,) +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'; +--connection default +SELECT user(); +CREATE TABLE t1remote (a INT NOT NULL); +INSERT INTO t1remote VALUES (10),(20),(30); +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'; +SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES ('xxx'); +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1 WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET a='yyy' WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 READONLY=1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; + +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--connection default +SELECT user(); +CREATE VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1 VALUES (2); +--error ER_ACCESS_DENIED_ERROR +UPDATE v1 SET a=123; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1; + +--disconnect user +--connection default +SELECT user(); +DROP VIEW v1; +DROP TABLE t1, t1remote; +DROP USER user@localhost; +--echo # +--echo # Testing FILE privileges done +--echo # + +# TODO: remote VARCHAR is displayed as CHAR + +CREATE TABLE t1 (a int, b char(10)); +INSERT INTO t1 VALUES (NULL,NULL),(0,'test00'),(1,'test01'),(2,'test02'),(3,'test03'); +SELECT * FROM t1; + +--echo # +--echo # Testing errors +--echo # + +# Bad user name +# Suppress "mysql_real_connect failed:" (printed in _DEBUG build) +--replace_result $PORT PORT "mysql_real_connect failed: " "" +--error ER_UNKNOWN_ERROR +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root1,port=$PORT' + +# Bad database name +--replace_result $PORT PORT "mysql_real_connect failed: " "" +--error ER_UNKNOWN_ERROR +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL DBNAME='unknown' TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' + +# Bad database name, with OPTION_LIST going first. +--replace_result $PORT PORT "mysql_real_connect failed: " "" +--error ER_UNKNOWN_ERROR +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL OPTION_LIST='host=localhost,user=root,port=$PORT' DBNAME='unknown' TABNAME='t1' + +# Bad table name +--replace_result $PORT PORT +--error ER_UNKNOWN_ERROR +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='unknown' OPTION_LIST='host=localhost,user=root,port=$PORT' +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE t2; + +# Bad column name +--replace_result $PORT PORT +--eval CREATE TABLE t2 (x int, y char(10)) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +--error ER_GET_ERRMSG +SELECT * FROM t2; +DROP TABLE t2; + +# The remote table disappeared +ALTER TABLE t1 RENAME t1backup; +--error ER_NO_SUCH_TABLE +SELECT * FROM t2; +ALTER TABLE t1backup RENAME t1; + + + +--echo # +--echo # Testing SELECT, etc. +--echo # + +# Automatic table structure +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + + +# Explicit table structure +--replace_result $PORT PORT +--eval CREATE TABLE t2 (a int, b char(10)) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + + +# Explicit table structure: remote NULL, local NOT NULL +--replace_result $PORT PORT +--eval CREATE TABLE t2 (a INT NOT NULL, b CHAR(10) NOT NULL) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + + +# Explicit table structure with wrong column types +--replace_result $PORT PORT +--eval CREATE TABLE t2 (a char(10), b int) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2; + +DROP TABLE t1; + +--echo # +--echo # Testing numeric data types +--echo # + +# TODO: tinyint is mapped to smallint +#CREATE TABLE t1 (a tinyint); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: unsigned does not work +#CREATE TABLE t1 (a tinyint unsigned); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +CREATE TABLE t1 (a smallint); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + +CREATE TABLE t1 (a mediumint); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + +CREATE TABLE t1 (a int); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + + +# TODO: bigint is mapped to double(20,0) +CREATE TABLE t1 (a bigint); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + + +# TODO: ERROR 1439: Display width out of range for 'a' (max = 255) +#CREATE TABLE t1 (a float); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1439: Display width out of range for 'a' (max = 255) +#CREATE TABLE t1 (a double); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: decimal is converted to double +#CREATE TABLE t1 (a decimal(20,5)); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: add test for BIT + +--echo # +--echo # Testing character data types +--echo # + +# TODO: char is mapped to varchar +CREATE TABLE t1 (a char(10)); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + +CREATE TABLE t1 (a varchar(10)); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type tinytext +#CREATE TABLE t1 (a tinytext); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type mediumtext +#CREATE TABLE t1 (a mediumtext); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: text is converted to varchar(256) +#CREATE TABLE t1 (a text); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type longtext +#CREATE TABLE t1 (a longtext); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +#TODO: add tests for ENUM +#TODO: add tests for SET + +--echo # +--echo # Testing binary data types +--echo # + +# TODO: ERROR 1105: Unsupported column type binary +#CREATE TABLE t1 (a binary(10)); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type varbinary +#CREATE TABLE t1 (a varbinary(10)); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type tinyblob +#CREATE TABLE t1 (a tinyblob); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type mediumblob +#CREATE TABLE t1 (a mediumblob); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: blob is converted to varchar(256) +#CREATE TABLE t1 (a blob); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type longblob +#CREATE TABLE t1 (a longblob); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: ERROR 1105: Unsupported column type geometry +#CREATE TABLE t1 (a geometry); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +--echo # +--echo # Testing temporal data types +--echo # + +# TODO: time is converted to date +#CREATE TABLE t1 (a time); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +CREATE TABLE t1 (a date); +--replace_result $PORT PORT +--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +--replace_result $PORT PORT +SHOW CREATE TABLE t1; +--replace_result $PORT PORT +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t2, t1; + +# TODO: datetime is converted to date +#CREATE TABLE t1 (a datetime); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: timestamp is converted to date +#CREATE TABLE t1 (a timestamp); +#--replace_result $PORT PORT +#--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' +#--replace_result $PORT PORT +#SHOW CREATE TABLE t1; +#--replace_result $PORT PORT +#SHOW CREATE TABLE t2; +#SELECT * FROM t2; +#DROP TABLE t2, t1; + +# TODO: add test for YEAR +# TODO: add tests for fractional seconds + diff --git a/storage/connect/mysql-test/connect/t/null.test b/storage/connect/mysql-test/connect/t/null.test new file mode 100644 index 00000000000..3d1e33eb77c --- /dev/null +++ b/storage/connect/mysql-test/connect/t/null.test @@ -0,0 +1,87 @@ +let $MYSQLD_DATADIR= `select @@datadir`;
+
+--echo #
+--echo # Testing FIX null columns
+--echo #
+CREATE TABLE t1
+(
+ id INT NOT NULL,
+ nb INT,
+ msg VARCHAR(12)
+) ENGINE=CONNECT TABLE_TYPE=FIX;
+--error ER_BAD_NULL_ERROR
+INSERT INTO t1 values(NULL,1,'Hello');
+INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero');
+SELECT * FROM t1;
+SELECT* FROM t1 WHERE id IS NULL;
+SELECT * FROM t1 WHERE nb IS NULL;
+SELECT * FROM t1 WHERE msg IS NOT NULL;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing CSV null columns
+--echo #
+CREATE TABLE t1
+(
+ id INT NOT NULL,
+ nb INT,
+ msg VARCHAR(12)
+) ENGINE=CONNECT TABLE_TYPE=CSV HEADER=1;
+--error ER_BAD_NULL_ERROR
+INSERT INTO t1 values(NULL,1,'Hello');
+INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero');
+SELECT * FROM t1;
+SELECT* FROM t1 WHERE id IS NULL;
+SELECT * FROM t1 WHERE nb IS NULL;
+SELECT * FROM t1 WHERE msg IS NOT NULL;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing BIN null columns
+--echo #
+CREATE TABLE t1
+(
+ id INT NOT NULL,
+ nb INT,
+ msg VARCHAR(12)
+) ENGINE=CONNECT TABLE_TYPE=BIN;
+--error ER_BAD_NULL_ERROR
+INSERT INTO t1 values(NULL,1,'Hello');
+INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero');
+SELECT * FROM t1;
+SELECT* FROM t1 WHERE id IS NULL;
+SELECT * FROM t1 WHERE nb IS NULL;
+SELECT * FROM t1 WHERE msg IS NOT NULL;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing DBF null columns
+--echo #
+CREATE TABLE t1
+(
+ id INT NOT NULL,
+ nb INT,
+ msg VARCHAR(12)
+) ENGINE=CONNECT TABLE_TYPE=DBF;
+--error ER_BAD_NULL_ERROR
+INSERT INTO t1 values(NULL,1,'Hello');
+INSERT INTO t1 values(10,4,NULL),(20,2,'Hello'),(0,0,'Zero');
+SELECT * FROM t1;
+SELECT* FROM t1 WHERE id IS NULL;
+SELECT * FROM t1 WHERE nb IS NULL;
+SELECT * FROM t1 WHERE msg IS NOT NULL;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing INI null columns
+--echo #
+CREATE TABLE t1
+(
+ `sec` char(8) NOT NULL flag=1,
+ `key` char(12)
+) ENGINE=CONNECT TABLE_TYPE=INI;
+INSERT INTO t1(sec) values('S1');
+SELECT * FROM t1;
+INSERT INTO t1 values('S1','Newval');
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/t/odbc_sqlite3.test b/storage/connect/mysql-test/connect/t/odbc_sqlite3.test new file mode 100644 index 00000000000..91b9c230c69 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/odbc_sqlite3.test @@ -0,0 +1,107 @@ +# +# To run this test, install SQLite3 ODBC Driver from +# http://www.ch-werner.de/sqliteodbc/ +# The installer file is sqliteodbc.exe +# Version sqliteodbc-0.991 is known to Work. +# +# On Windows the test should start working automatically +# +# On Linux add these lines into /etc/odbcinst.ini +# +#[SQLite3 ODBC Driver] +#Description=SQLite3 ODBC Driver +#Driver=/opt/sqliteodbc/libsqlite3odbc.so +#Setup=/opt/sqliteodbc/libsqlite3odbc.so +# +# (adjust the directory "/opt/sqliteodbc/" according to your OS settings) +# +# Note, the test does not need a DSN to be created. +# + +--disable_query_log +--error 0,ER_UNKNOWN_ERROR +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Drivers; +if ($mysql_errno) +{ + Skip No ODBC support; +} +if (!`SELECT count(*) FROM t1 WHERE Description='SQLite3 ODBC Driver'`) +{ + DROP TABLE t1; + Skip Need SQLite3 ODBC Driver; +} +SHOW CREATE TABLE t1; +DROP TABLE t1; +--enable_query_log + +SET NAMES utf8; + +let $MYSQLD_DATADIR= `select @@datadir`; + + +GRANT ALL PRIVILEGES ON *.* TO user@localhost; +REVOKE FILE ON *.* FROM user@localhost; +--connect(user,localhost,user,,) +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC; +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Drivers; +--error ER_ACCESS_DENIED_ERROR +CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Sources; +--connection default +SELECT user(); + + +# +# For some reasons Windows does not allow to remove the data base +# file after "DROP TABLE t1". So unlike in odbc_xls.test we won't copy +# the data file, we'll use directly the file in std_data. +# As we do not do any modifications in the database, this should be OK. +# +let $Database=$MTR_SUITE_DIR/std_data/test.sqlite3; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +--eval CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='Driver=SQLite3 ODBC Driver;Database=$Database;NoWCHAR=yes' CHARSET=utf8 DATA_CHARSET=utf8; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +SHOW CREATE TABLE t1; +SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM t1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO t1 VALUES ('xxx'); +--error ER_ACCESS_DENIED_ERROR +DELETE FROM t1 WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +UPDATE t1 SET a='yyy' WHERE a='xxx'; +--error ER_ACCESS_DENIED_ERROR +TRUNCATE TABLE t1; +--error ER_ACCESS_DENIED_ERROR +ALTER TABLE t1 READONLY=1; +--error ER_ACCESS_DENIED_ERROR +CREATE VIEW v1 AS SELECT * FROM t1; + +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--connection default +SELECT user(); +CREATE VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +--error ER_ACCESS_DENIED_ERROR +SELECT * FROM v1; +--error ER_ACCESS_DENIED_ERROR +INSERT INTO v1 VALUES (2); +--error ER_ACCESS_DENIED_ERROR +UPDATE v1 SET a=123; +--error ER_ACCESS_DENIED_ERROR +DELETE FROM v1; + +--disconnect user +--connection default +SELECT user(); +DROP VIEW v1; +DROP TABLE t1; + +DROP USER user@localhost; diff --git a/storage/connect/mysql-test/connect/t/odbc_xls.test b/storage/connect/mysql-test/connect/t/odbc_xls.test new file mode 100644 index 00000000000..449d8983d63 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/odbc_xls.test @@ -0,0 +1,26 @@ +--disable_query_log +--error 0,ER_UNKNOWN_ERROR +CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CATFUNC=Sources; +if ($mysql_errno) +{ + Skip No ODBC support; +} +if (!`SELECT count(*) FROM t1 WHERE Name='ConnectEngineXLS'`) +{ + DROP TABLE t1; + Skip Need ODBC data source ConnectEngineXLS; +} +SHOW CREATE TABLE t1; +DROP TABLE t1; +--enable_query_log + +let $MYSQLD_DATADIR= `select @@datadir`; + +--copy_file $MTR_SUITE_DIR/std_data/contacts.xls $MYSQLD_DATADIR/test/contacts.xls + +--replace_result $MYSQLD_DATADIR DATADIR +--eval CREATE TABLE contact (Nom VARCHAR(128), Fonction VARCHAR(128), Company VARCHAR(128), Repertoire VARCHAR(30)) ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='DSN=ConnectEngineXLS;DBQ=$MYSQLD_DATADIR/test/contacts.xls'; +SELECT Nom, Fonction FROM contact WHERE Repertoire='ascii'; +DROP TABLE contact; + +--remove_file $MYSQLD_DATADIR/test/contacts.xls diff --git a/storage/connect/mysql-test/connect/t/secure_file_priv-master.opt b/storage/connect/mysql-test/connect/t/secure_file_priv-master.opt new file mode 100644 index 00000000000..e9a43a5584d --- /dev/null +++ b/storage/connect/mysql-test/connect/t/secure_file_priv-master.opt @@ -0,0 +1 @@ +--secure_file_priv=$MYSQL_TMP_DIR diff --git a/storage/connect/mysql-test/connect/t/secure_file_priv.test b/storage/connect/mysql-test/connect/t/secure_file_priv.test new file mode 100644 index 00000000000..46633502034 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/secure_file_priv.test @@ -0,0 +1,13 @@ +let $DATADIR= `select @@datadir`; +let $SECUREDIR= `select @@secure_file_priv`; + +--replace_result $DATADIR DATADIR +--error ER_OPTION_PREVENTS_STATEMENT +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='$DATADIR/t1.dbf' + +--replace_result $SECUREDIR SECUREDATADIR +--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=DBF FILE_NAME='$SECUREDIR/t1.dbf' +INSERT INTO t1 VALUES (10); +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $SECUREDIR/t1.dbf diff --git a/storage/connect/mysql-test/connect/t/tbl.test b/storage/connect/mysql-test/connect/t/tbl.test new file mode 100644 index 00000000000..e802e049c48 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/tbl.test @@ -0,0 +1,53 @@ +let $MYSQLD_DATADIR= `select @@datadir`;
+
+--echo #
+--echo # Checking TBL tables
+--echo #
+CREATE TABLE t1 (
+a INT NOT NULL,
+message CHAR(10)) ENGINE=connect;
+INSERT INTO t1 VALUES (1,'Testing'),(2,'dos table'),(3,'t1');
+SELECT * FROM t1;
+
+CREATE TABLE t2 (
+a INT NOT NULL,
+message CHAR(10)) ENGINE=connect TABLE_TYPE=BIN;
+INSERT INTO t2 VALUES (1,'Testing'),(2,NULL),(3,'t2');
+SELECT * FROM t2;
+
+CREATE TABLE t3 (
+a INT NOT NULL,
+message CHAR(10)) ENGINE=connect TABLE_TYPE=CSV;
+INSERT INTO t3 VALUES (1,'Testing'),(2,'csv table'),(3,'t3');
+SELECT * FROM t3;
+
+CREATE TABLE t4 (
+ta INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+message CHAR(20)) ENGINE=MyISAM;
+INSERT INTO t4 (message) VALUES ('Testing'),('myisam table'),('t4');
+SELECT * FROM t4;
+
+CREATE TABLE total (
+tabname CHAR(8) NOT NULL SPECIAL='TABID',
+ta TINYINT NOT NULL FLAG=1,
+message CHAR(20))
+engine=CONNECT table_type=TBL table_list='t1,t2,t3,t4';
+
+select * from total;
+select * from total where tabname = 't2';
+select * from total where tabname = 't2' and ta = 3;
+select * from total where tabname in ('t1','t4');
+select * from total where ta = 3 and tabname in ('t1','t2');
+select * from total where tabname <> 't2';
+select * from total where tabname != 't2' and ta = 3;
+select * from total where tabname not in ('t2','t3');
+select * from total where ta = 3 and tabname in ('t2','t3');
+select * from total where ta = 3 or tabname in ('t2','t4');
+select * from total where not tabname = 't2';
+select * from total where tabname = 't2' or tabname = 't1';
+
+DROP TABLE total;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE t4;
diff --git a/storage/connect/mysql-test/connect/t/upd.test b/storage/connect/mysql-test/connect/t/upd.test new file mode 100644 index 00000000000..f6461bfed96 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/upd.test @@ -0,0 +1,153 @@ +let $MYSQLD_DATADIR= `select @@datadir`;
+--copy_file $MTR_SUITE_DIR/std_data/employee.dat $MYSQLD_DATADIR/test/employee.dat
+
+CREATE TABLE employee
+(
+serialno CHAR(5) NOT NULL,
+name VARCHAR(12) NOT NULL FLAG=6,
+sex TINYINT(1) NOT NULL,
+title VARCHAR(15) NOT NULL FLAG=20,
+manager CHAR(5) NOT NULL,
+department CHAR(4) NOT NULL FLAG=41,
+secretary CHAR(5) NOT NULL FLAG=46,
+salary DOUBLE(8,2) NOT NULL FLAG=52
+) ENGINE=connect TABLE_TYPE=fix FILE_NAME='employee.dat' ENDING=1;
+SELECT * FROM employee;
+
+DELIMITER //;
+CREATE PROCEDURE test.tst_up() DETERMINISTIC
+BEGIN
+SELECT * FROM t1;
+UPDATE t1 SET salary = salary + 1, title = 'RESEARCH' WHERE title = 'SCIENTIST';
+UPDATE t1 SET salary = salary + 1, title = 'TECHNICIAN' WHERE title = 'ENGINEER';
+UPDATE t1 SET title = 'PUPPET' WHERE name = 'TONGHO';
+UPDATE t1 SET salary = 0. WHERE title = 'XXX';
+SELECT * FROM t1;
+DELETE FROM t1 WHERE title = 'SECRETARY';
+DELETE FROM t1 WHERE title = 'DIRECTOR';
+DELETE FROM t1 WHERE title = 'TYPIST';
+SELECT * FROM t1;
+DELETE FROM t1 LIMIT 3;
+INSERT INTO t1(serialno, name, title, salary) VALUES('66666','NEWMAN','ENGINEER',10000.80);
+SELECT * FROM t1;
+DROP TABLE t1;
+END//
+DELIMITER ;//
+
+--echo #
+--echo # Testing DOS table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing DOS table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect mapped=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing FIX table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing FIX table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix mapped=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing FIX table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=fix huge=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing CSV table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=csv AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing CSV table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=csv mapped=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing DBF table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=dbf AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing DBF table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=dbf mapped=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing BIN table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing BIN table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin mapped=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing BIN table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=bin huge=yes AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing VEC table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec MAX_ROWS=30 AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing VEC table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec mapped=yes MAX_ROWS=30 AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing VEC table changes
+--echo #
+CREATE TABLE t1 ENGINE=connect TABLE_TYPE=vec huge=yes MAX_ROWS=30 AS SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing INI table changes
+--echo #
+CREATE TABLE t1
+(
+serialno CHAR(5) NOT NULL FLAG=1,
+name VARCHAR(12) NOT NULL,
+sex TINYINT(1),
+title VARCHAR(15) NOT NULL,
+manager CHAR(5),
+department CHAR(4),
+secretary CHAR(5),
+salary DOUBLE(8,2) NOT NULL
+) ENGINE=connect TABLE_TYPE=ini;
+INSERT INTO t1 SELECT * FROM employee;
+CALL test.tst_up();
+
+--echo #
+--echo # Testing XML table changes (must be in a separate test)
+--echo #
+#CREATE TABLE t1 ENGINE=connect TABLE_TYPE=xml option_list='rownode=dd' AS SELECT * FROM employee;
+#CALL test.tst_up();
+
+DROP PROCEDURE test.tst_up;
+DROP TABLE employee;
+
+--remove_file $MYSQLD_DATADIR/test/employee.dat
diff --git a/storage/connect/mysql-test/connect/t/vec.test b/storage/connect/mysql-test/connect/t/vec.test new file mode 100644 index 00000000000..79ed4eabd86 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/vec.test @@ -0,0 +1,84 @@ +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=VEC MAX_ROWS=100; +let $FILE_EXT=VEC; +--source grant.inc + +CREATE TABLE dir1 ( + spath VARCHAR(256) NOT NULL flag=1, + fname VARCHAR(256) NOT NULL, + ftype CHAR(4) NOT NULL, + size DOUBLE(12,0) NOT NULL flag=5 +) ENGINE=CONNECT TABLE_TYPE=DIR FILE_NAME='*vec*'; + + +CREATE TABLE t1 +( + a INT NOT NULL, + b CHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=VEC FILE_NAME='t1vec'; +SHOW CREATE TABLE t1; +# Testing SELECT on empty file +--replace_regex /Open.rb. error 2 on .*\/test\/t1vec/Open(rb) error 2 on DATADIR\/test\/t1vec/ +SELECT * FROM t1; +INSERT INTO t1 VALUES (0,'test01'), (1,'test01'), (2,'test02'), (3,'test03'); +SELECT * FROM t1; +SELECT a FROM t1; +SELECT b FROM t1; +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1vec1 +--remove_file $MYSQLD_DATADIR/test/t1vec2 + + +CREATE TABLE t1 +( + a INT NOT NULL, + b CHAR(10) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=VEC FILE_NAME='t1vec' MAX_ROWS=10; +SHOW CREATE TABLE t1; +# Testing SELECTs on empty file +--replace_regex /Open.rb. error 2 on .*\/test\/t1vec/Open(rb) error 2 on DATADIR\/test\/t1vec/ +SELECT * FROM t1; +--replace_regex /Open.rb. error 2 on .*\/test\/t1vec/Open(rb) error 2 on DATADIR\/test\/t1vec/ +SELECT a FROM t1; +--replace_regex /Open.rb. error 2 on .*\/test\/t1vec/Open(rb) error 2 on DATADIR\/test\/t1vec/ +SELECT b FROM t1; +INSERT INTO t1 VALUES (0,'test01'), (1,'test01'), (2,'test02'), (3,'test03'); +SELECT * FROM t1; +SELECT a FROM t1; +SELECT b FROM t1; +--replace_result $MYSQLD_DATADIR DATADIR/ +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +--echo # +--echo # Testing READONLY +--echo # +ALTER TABLE t1 READONLY=yes; +SHOW CREATE TABLE t1; +--error ER_GET_ERRMSG +INSERT INTO t1 VALUES (4,'test04'); +--error ER_GET_ERRMSG +UPDATE t1 SET b='test04' WHERE a=3; +--error ER_GET_ERRMSG +DELETE FROM t1 WHERE a=3; +--error ER_GET_ERRMSG +TRUNCATE TABLE t1; +ALTER TABLE t1 READONLY=no; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (4,'test04'); +UPDATE t1 SET b='test04a' WHERE a=4; +DELETE FROM t1 WHERE a=0; +SELECT * FROM t1; +TRUNCATE TABLE t1; +SELECT fname, ftype, size FROM dir1 ORDER BY fname, ftype; +SELECT * FROM t1; +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/t1vec +--remove_file $MYSQLD_DATADIR/test/t1vec.blk + + +--echo # +--echo # Clean up +--echo # +DROP TABLE dir1; diff --git a/storage/connect/mysql-test/connect/t/xml.test b/storage/connect/mysql-test/connect/t/xml.test new file mode 100644 index 00000000000..96e7e1a3a97 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/xml.test @@ -0,0 +1,335 @@ +--disable_query_log +--error 0,ER_UNKNOWN_ERROR +CREATE TABLE t1 (a VARCHAR(10)) +ENGINE=CONNECT TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2'; +if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1' + AND ENGINE='CONNECT' + AND CREATE_OPTIONS LIKE '%`table_type`=XML%' + AND CREATE_OPTIONS LIKE '%xmlsup=libxml2%'`) +{ + Skip Need LIBXML2; +} +DROP TABLE t1; +--enable_query_log + + +let $MYSQLD_DATADIR= `select @@datadir`; + +let $TABLE_OPTIONS=TABLE_TYPE=XML OPTION_LIST='xmlsup=libxml2,rownode=row'; +let $FILE_EXT=XML; +--source grant.inc + + +SET NAMES utf8; + +--vertical_results + +--copy_file $MTR_SUITE_DIR/std_data/xsample.xml $MYSQLD_DATADIR/test/xsample.xml +--copy_file $MTR_SUITE_DIR/std_data/latin1.xml $MYSQLD_DATADIR/test/latin1.xml +--copy_file $MTR_SUITE_DIR/std_data/cp1251.xml $MYSQLD_DATADIR/test/cp1251.xml + +#--echo $MYSQL_TEST_DIR +#--exec pwd +#SELECT LOAD_FILE('test/xsample.xml'); + + +--echo # +--echo # Testing tag values +--echo # +CREATE TABLE t1 +( + AUTHOR CHAR(50), + TITLE CHAR(32), + TRANSLATOR CHAR(40), + PUBLISHER CHAR(40), + DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing that tag names are case sensitive +--echo # +CREATE TABLE t1 +( + author CHAR(50), + TITLE CHAR(32), + TRANSLATOR CHAR(40), + PUBLISHER CHAR(40), + DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing attribute values +--echo # +CREATE TABLE t1 ( + ISBN CHAR(15), + LANG CHAR(2), + SUBJECT CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='Coltype=@,xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing that attribute names are case sensitive +--echo # +CREATE TABLE t1 ( + isbn CHAR(15), + LANG CHAR(2), + SUBJECT CHAR(32) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + OPTION_LIST='Coltype=@,xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing mixed tag and attribute values +--echo # +CREATE TABLE t1 ( + ISBN CHAR(15) FIELD_FORMAT='@', + LANG CHAR(2) FIELD_FORMAT='@', + SUBJECT CHAR(32) FIELD_FORMAT='@', + AUTHOR CHAR(50), + TITLE CHAR(32), + TRANSLATOR CHAR(40), + PUBLISHER CHAR(40), + DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK' + OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing INSERT on mixed tag and attribute values +--echo # +--copy_file $MTR_SUITE_DIR/std_data/xsample.xml $MYSQLD_DATADIR/test/xsample2.xml +--chmod 0644 $MYSQLD_DATADIR/test/xsample2.xml +CREATE TABLE t1 ( + ISBN CHAR(15) FIELD_FORMAT='@', + LANG CHAR(2) FIELD_FORMAT='@', + SUBJECT CHAR(32) FIELD_FORMAT='@', + AUTHOR CHAR(50), + TITLE CHAR(32), + TRANSLATOR CHAR(40), + PUBLISHER CHAR(40), + DATEPUB INT(4) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.xml' + TABNAME='BIBLIO' + OPTION_LIST='rownode=BOOK,xmlsup=libxml2'; +INSERT INTO t1 (ISBN, LANG, SUBJECT, AUTHOR, TITLE, PUBLISHEr, DATEPUB) +VALUES('9782212090529','fr','général','Alain Michard', +'XML, Langage et Applications','Eyrolles Paris',1998); +SELECT * FROM t1; +SELECT LOAD_FILE('test/xsample2.xml'); +DROP TABLE t1; +--remove_file $MYSQLD_DATADIR/test/xsample2.xml + + +--echo # +--echo # Testing XPath +--echo # +CREATE TABLE t1 ( + isbn CHAR(15) FIELD_FORMAT='@ISBN', + language CHAR(2) FIELD_FORMAT='@LANG', + subject CHAR(32) FIELD_FORMAT='@SUBJECT', + authorfn CHAR(20) FIELD_FORMAT='AUTHOR/FIRSTNAME', + authorln CHAR(20) FIELD_FORMAT='AUTHOR/LASTNAME', + title CHAR(32) FIELD_FORMAT='TITLE', + translated CHAR(32) FIELD_FORMAT='TRANSLATOR/@PREFIX', + tranfn CHAR(20) FIELD_FORMAT='TRANSLATOR/FIRSTNAME', + tranln CHAR(20) FIELD_FORMAT='TRANSLATOR/LASTNAME', + publisher CHAR(20) FIELD_FORMAT='PUBLISHER/NAME', + location CHAR(20) FIELD_FORMAT='PUBLISHER/PLACE', + year INT(4) FIELD_FORMAT='DATEPUB' +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1,xmlsup=libxml2'; +SELECT * FROM t1; +SELECT isbn, title, translated, tranfn, tranln, location FROM t1 +WHERE translated <> ''; +DROP TABLE t1; + + +# +# TODO: Connect.pdf says nodes with variable depth are not supported +# +#--echo # +#--echo # Relative paths are not supported +#--echo # +#CREATE TABLE t1 ( +# authorfn CHAR(20) FIELD_FORMAT='//FIRSTNAME', +# authorln CHAR(20) FIELD_FORMAT='//LASTNAME' +#) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' +# TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1'; +#SELECT * FROM t1; +#DROP TABLE t1; + + +# +# TODO: Connect.pdf says absolute paths are not supported +# +#--echo # +#--echo # Absolute path is not supported +#--echo # +#CREATE TABLE t1 ( +# authorfn CHAR(20) FIELD_FORMAT='/BIBLIO/BOOK/AUTHOR/FIRSTNAME', +# authorln CHAR(20) FIELD_FORMAT='/BIBLIO/BOOK/AUTHOR/LASTNAME' +#) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' +# TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1'; +#SELECT * FROM t1; +#DROP TABLE t1; + + +--echo # +--echo # Testing that XPath is case sensitive +--echo # +CREATE TABLE t1 +( + isbn CHAR(15) FIELD_FORMAT='@isbn' +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample.xml' + TABNAME='BIBLIO' OPTION_LIST='rownode=BOOK,skipnull=1,xmlsup=libxml2'; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing character sets +--echo # + +--error ER_UNKNOWN_ERROR +CREATE TABLE t1 +( + c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2' + DATA_CHARSET=latin1; + +CREATE TABLE t1 +( + c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2' + DATA_CHARSET=utf8; +SHOW CREATE TABLE t1; +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + c CHAR(16) +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 +( + c CHAR(16) CHARACTER SET utf8 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Conversion from latin1 to cp1251 produces a warning. +--echo # Question marks are returned. +--echo # +CREATE TABLE t1 +( + c CHAR(16) CHARACTER SET cp1251 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='latin1.xml' + OPTION_LIST='xmlsup=libxml2'; +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing Cyrillic +--echo # +CREATE TABLE t1 +( + c CHAR(16) CHARACTER SET utf8 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='cp1251.xml' + OPTION_LIST='xmlsup=libxml2,rownode=b'; +SELECT * FROM t1; +INSERT INTO t1 VALUES ('ИКЛМÐ'); +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; +CREATE TABLE t1 +( + c CHAR(16) CHARACTER SET cp1251 +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='cp1251.xml' + OPTION_LIST='xmlsup=libxml2,rownode=b'; +SELECT * FROM t1; +INSERT INTO t1 VALUES ('ОПРСТ'); +SELECT c, HEX(c) FROM t1; +DROP TABLE t1; + + +--echo # +--echo # Testing that the underlying file is created with a proper Encoding +--echo # +CREATE TABLE t1 (node VARCHAR(50)) + CHARACTER SET latin1 + ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=utf-8'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +SELECT node, hex(node) FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.xml +SET @a=LOAD_FILE('test/t1.xml'); +SELECT LEFT(@a,38); +SELECT HEX(EXTRACTVALUE(@a,'/t1/line/node')); +--remove_file $MYSQLD_DATADIR/test/t1.xml + +CREATE TABLE t1 (node VARCHAR(50)) + CHARACTER SET latin1 + ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=iso-8859-1'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +SELECT node, hex(node) FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.xml +SET @a=LOAD_FILE('test/t1.xml'); +SELECT LEFT(@a,43); +SELECT HEX(EXTRACTVALUE(@a,'/t1/line/node')); +--remove_file $MYSQLD_DATADIR/test/t1.xml + + +--echo # +--echo # Testing XML entities +--echo # +CREATE TABLE t1 (node VARCHAR(50)) + CHARACTER SET utf8 + ENGINE=connect TABLE_TYPE=xml FILE_NAME='t1.xml' + OPTION_LIST='xmlsup=libxml2,rownode=line,encoding=iso-8859-1'; +INSERT INTO t1 VALUES (_latin1 0xC0C1C2C3); +INSERT INTO t1 VALUES (_cp1251 0xC0C1C2C3); +INSERT INTO t1 VALUES ('&<>"\''); +SELECT node, hex(node) FROM t1; +DROP TABLE t1; +--chmod 0777 $MYSQLD_DATADIR/test/t1.xml +SET @a=LOAD_FILE('test/t1.xml'); +SELECT CAST(@a AS CHAR CHARACTER SET latin1); +--remove_file $MYSQLD_DATADIR/test/t1.xml + + + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/xsample.xml +--remove_file $MYSQLD_DATADIR/test/latin1.xml +--remove_file $MYSQLD_DATADIR/test/cp1251.xml diff --git a/storage/connect/myutil.cpp b/storage/connect/myutil.cpp new file mode 100644 index 00000000000..0b97e8aa5a0 --- /dev/null +++ b/storage/connect/myutil.cpp @@ -0,0 +1,200 @@ +/************** MyUtil C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: MYUTIL */ +/* ------------- */ +/* Version 1.1 */ +/* */ +/* Author Olivier BERTRAND 2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* It contains utility functions to convert data types. */ +/* It can optionally use the embedded MySQL library. */ +/* */ +/************************************************************************/ +#include "my_global.h" +#include <mysql.h> +#if defined(WIN32) +//#include <windows.h> +#else // !WIN32 +#include "osutil.h" +#endif // !WIN32 + +#include "global.h" +#include "plgdbsem.h" +//#include "value.h" +//#include "valblk.h" +#define DLL_EXPORT // Items are exported from this DLL + +/************************************************************************/ +/* Convert from MySQL type name to PlugDB type number */ +/************************************************************************/ +int MYSQLtoPLG(char *typname) + { + int type; + + if (!stricmp(typname, "int") || !stricmp(typname, "mediumint") || + !stricmp(typname, "integer")) + type = TYPE_INT; + else if (!stricmp(typname, "smallint")) + type = TYPE_SHORT; + else if (!stricmp(typname, "char") || !stricmp(typname, "varchar") || + !stricmp(typname, "text") || !stricmp(typname, "blob")) + type = TYPE_STRING; + else if (!stricmp(typname, "double") || !stricmp(typname, "float") || + !stricmp(typname, "real") || + !stricmp(typname, "decimal") || !stricmp(typname, "numeric")) + type = TYPE_FLOAT; + else if (!stricmp(typname, "date") || !stricmp(typname, "datetime") || + !stricmp(typname, "time") || !stricmp(typname, "timestamp") || + !stricmp(typname, "year")) + type = TYPE_DATE; + else if (!stricmp(typname, "bigint") || !stricmp(typname, "longlong")) + type = TYPE_BIGINT; + else if (!stricmp(typname, "tinyint")) + type = TYPE_TINY; + else + type = TYPE_ERROR; + + return type; + } // end of MYSQLtoPLG + +/************************************************************************/ +/* Convert from PlugDB type to MySQL type number */ +/************************************************************************/ +enum enum_field_types PLGtoMYSQL(int type, bool dbf) + { + enum enum_field_types mytype; + + switch (type) { + case TYPE_INT: + mytype = MYSQL_TYPE_LONG; + break; + case TYPE_SHORT: + mytype = MYSQL_TYPE_SHORT; + break; + case TYPE_FLOAT: + mytype = MYSQL_TYPE_DOUBLE; + break; + case TYPE_DATE: + mytype = (dbf) ? MYSQL_TYPE_DATE : MYSQL_TYPE_DATETIME; + break; + case TYPE_STRING: + mytype = MYSQL_TYPE_VARCHAR; + break; + case TYPE_BIGINT: + mytype = MYSQL_TYPE_LONGLONG; + break; + case TYPE_TINY: + mytype = MYSQL_TYPE_TINY; + break; + default: + mytype = MYSQL_TYPE_NULL; + } // endswitch mytype + + return mytype; + } // end of PLGtoMYSQL + +/************************************************************************/ +/* Convert from MySQL type to PlugDB type number */ +/************************************************************************/ +int MYSQLtoPLG(int mytype) + { + int type; + + switch (mytype) { + case MYSQL_TYPE_SHORT: + type = TYPE_SHORT; + break; + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_ENUM: // ??? + type = TYPE_INT; + break; + case MYSQL_TYPE_LONGLONG: + type = TYPE_BIGINT; + break; + case MYSQL_TYPE_TINY: + type = TYPE_TINY; + break; + case MYSQL_TYPE_DECIMAL: +#if !defined(ALPHA) + case MYSQL_TYPE_NEWDECIMAL: +#endif // !ALPHA) + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + type = TYPE_FLOAT; + break; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TIME: + type = TYPE_DATE; + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: +#if !defined(ALPHA) + case MYSQL_TYPE_VARCHAR: +#endif // !ALPHA) + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + type = TYPE_STRING; + break; + default: + type = TYPE_ERROR; + } // endswitch mytype + + return type; + } // end of MYSQLtoPLG + +/************************************************************************/ +/* Returns the format corresponding to a MySQL date type number. */ +/************************************************************************/ +char *MyDateFmt(int mytype) + { + char *fmt; + + switch (mytype) { + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + fmt = "YYYY-MM-DD hh:mm:ss"; + break; + case MYSQL_TYPE_DATE: + fmt = "YYYY-MM-DD"; + break; + case MYSQL_TYPE_YEAR: + fmt = "YYYY"; + break; + case MYSQL_TYPE_TIME: + fmt = "hh:mm:ss"; + break; + default: + fmt = NULL; + } // endswitch mytype + + return fmt; + } // end of MyDateFmt + +/************************************************************************/ +/* Returns the format corresponding to a MySQL date type name. */ +/************************************************************************/ +char *MyDateFmt(char *typname) + { + char *fmt; + + if (!stricmp(typname, "datetime") || !stricmp(typname, "timestamp")) + fmt = "YYYY-MM-DD hh:mm:ss"; + else if (!stricmp(typname, "date")) + fmt = "YYYY-MM-DD"; + else if (!stricmp(typname, "year")) + fmt = "YYYY"; + else if (!stricmp(typname, "time")) + fmt = "hh:mm:ss"; + else + fmt = NULL; + + return fmt; + } // end of MyDateFmt + diff --git a/storage/connect/myutil.h b/storage/connect/myutil.h new file mode 100644 index 00000000000..d3d421418ca --- /dev/null +++ b/storage/connect/myutil.h @@ -0,0 +1,8 @@ +/***********************************************************************/ +/* Prototypes of Functions used externally. */ +/***********************************************************************/ +enum enum_field_types PLGtoMYSQL(int type, bool dbf); +int MYSQLtoPLG(char *typname); +int MYSQLtoPLG(int mytype); +char *MyDateFmt(int mytype); +char *MyDateFmt(char *typname); diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h new file mode 100644 index 00000000000..3665d1b283e --- /dev/null +++ b/storage/connect/odbccat.h @@ -0,0 +1,8 @@ +/***********************************************************************/ +/* ODBC catalog function prototypes. */ +/***********************************************************************/ +PQRYRES ODBCDataSources(PGLOBAL g, bool info); +PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, + char *colpat, bool info); +PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info); +PQRYRES ODBCDrivers(PGLOBAL g, bool info); diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp new file mode 100644 index 00000000000..6de2acef391 --- /dev/null +++ b/storage/connect/odbconn.cpp @@ -0,0 +1,2113 @@ +/************ Odbconn C++ Functions Source Code File (.CPP) ************/ +/* Name: ODBCONN.CPP Version 1.6 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */ +/* */ +/* This file contains the ODBC connection classes functions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//nclude <io.h> +//nclude <fcntl.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#else +#if defined(UNIX) +#include <errno.h> +#else +//nclude <io.h> +#endif +//nclude <fcntl.h> +#define NODW +#endif + +/***********************************************************************/ +/* Required objects includes. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" +//#include "kindex.h" +#include "xtable.h" +#include "tabodbc.h" +#include "plgcnx.h" // For DB types +#include "resource.h" +#include "valblk.h" + + +#if defined(WIN32) +/***********************************************************************/ +/* For dynamic load of ODBC32.DLL */ +/***********************************************************************/ +#pragma comment(lib, "odbc32.lib") +extern "C" HINSTANCE s_hModule; // Saved module handle +#else // !WIN32 +extern "C" int GetRcString(int id, char *buf, int bufsize); +#endif // !WIN32 + +/***********************************************************************/ +/* Some macro's (should be defined elsewhere to be more accessible) */ +/***********************************************************************/ +#if defined(_DEBUG) +#define ASSERT(f) assert(f) +#define DEBUG_ONLY(f) (f) +#else // !_DEBUG +#define ASSERT(f) ((void)0) +#define DEBUG_ONLY(f) ((void)0) +#endif // !_DEBUG + +extern "C" int trace; + +/***********************************************************************/ +/* GetSQLType: returns the SQL_TYPE corresponding to a PLG type. */ +/***********************************************************************/ +static short GetSQLType(int type) + { + short tp = SQL_TYPE_NULL; + + switch (type) { + case TYPE_STRING: tp = SQL_CHAR; break; + case TYPE_SHORT: tp = SQL_SMALLINT; break; + case TYPE_INT: tp = SQL_INTEGER; break; + case TYPE_DATE: tp = SQL_TIMESTAMP; break; + case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5) + case TYPE_FLOAT: tp = SQL_DOUBLE; break; + } // endswitch type + + return tp; + } // end of GetSQLType + +/***********************************************************************/ +/* GetSQLCType: returns the SQL_C_TYPE corresponding to a PLG type. */ +/***********************************************************************/ +static int GetSQLCType(int type) + { + int tp = SQL_TYPE_NULL; + + switch (type) { + case TYPE_STRING: tp = SQL_C_CHAR; break; + case TYPE_SHORT: tp = SQL_C_SHORT; break; + case TYPE_INT: tp = SQL_C_LONG; break; + case TYPE_DATE: tp = SQL_C_TIMESTAMP; break; + case TYPE_BIGINT: tp = SQL_C_SBIGINT; break; + case TYPE_FLOAT: tp = SQL_C_DOUBLE; break; + } // endswitch type + + return tp; + } // end of GetSQLCType + +/***********************************************************************/ +/* TranslateSQLType: translate a SQL Type to a PLG type. */ +/***********************************************************************/ +int TranslateSQLType(int stp, int prec, int& len) + { + int type; + + switch (stp) { + case SQL_CHAR: // 1 + case SQL_VARCHAR: // 12 + type = TYPE_STRING; + break; + case SQL_LONGVARCHAR: // (-1) + type = TYPE_STRING; + len = min(abs(len), 255); + break; + case SQL_NUMERIC: // 2 + case SQL_DECIMAL: // 3 + type = (prec) ? TYPE_FLOAT + : (len > 10) ? TYPE_BIGINT : TYPE_INT; + break; + case SQL_INTEGER: // 4 + type = TYPE_INT; + break; + case SQL_SMALLINT: // 5 + case SQL_TINYINT: // (-6) + case SQL_BIT: // (-7) + type = TYPE_SHORT; + break; + case SQL_FLOAT: // 6 + case SQL_REAL: // 7 + case SQL_DOUBLE: // 8 + type = TYPE_FLOAT; + break; + case SQL_DATETIME: // 9 +// case SQL_DATE: // 9 + type = TYPE_DATE; + len = 10; + break; + case SQL_INTERVAL: // 10 +// case SQL_TIME: // 10 + type = TYPE_STRING; + len = 8 + ((prec) ? (prec+1) : 0); + break; + case SQL_TIMESTAMP: // 11 + type = TYPE_DATE; + len = 19 + ((prec) ? (prec+1) : 0); + break; + case SQL_BIGINT: // (-5) + type = TYPE_BIGINT; + break; + case SQL_UNKNOWN_TYPE: // 0 + case SQL_BINARY: // (-2) + case SQL_VARBINARY: // (-3) + case SQL_LONGVARBINARY: // (-4) +// case SQL_BIT: // (-7) + case SQL_GUID: // (-11) + default: + type = TYPE_ERROR; + len = 0; + } // endswitch type + + return type; + } // end of TranslateSQLType + +/***********************************************************************/ +/* ODBConn static members initialization. */ +/***********************************************************************/ +//HENV ODBConn::m_henv = SQL_NULL_HENV; +//int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above + +/***********************************************************************/ +/* Allocate the structure used to refer to the result set. */ +/***********************************************************************/ +CATPARM *AllocCatInfo(PGLOBAL g, CATINFO fid, char *tab, PQRYRES qrp) + { + size_t i, m, n; + CATPARM *cap; + +#if defined(_DEBUG) + assert(qrp); +#endif + m = (size_t)qrp->Maxres; + n = (size_t)qrp->Nbcol; + cap = (CATPARM *)PlugSubAlloc(g, NULL, sizeof(CATPARM)); + memset(cap, 0, sizeof(CATPARM)); + cap->Id = fid; + cap->Qrp = qrp; + cap->Tab = (PUCHAR)tab; + cap->Vlen = (SQLLEN* *)PlugSubAlloc(g, NULL, n * sizeof(SDWORD *)); + + for (i = 0; i < n; i++) + cap->Vlen[i] = (SQLLEN *)PlugSubAlloc(g, NULL, m * sizeof(SDWORD)); + + cap->Status = (UWORD *)PlugSubAlloc(g, NULL, m * sizeof(UWORD)); + return cap; + } // end of AllocCatInfo + +/***********************************************************************/ +/* Check for nulls and reset them to Null (?) values. */ +/***********************************************************************/ +void ResetNullValues(CATPARM *cap) + { + int i, n, ncol; + PCOLRES crp; + PQRYRES qrp = cap->Qrp; + +#if defined(_DEBUG) + assert(qrp); +#endif + + ncol = qrp->Nbcol; + + for (i = 0, crp = qrp->Colresp; i < ncol && crp; i++, crp = crp->Next) + for (n = 0; n < qrp->Nblin; n++) + if (cap->Vlen[i][n] == SQL_NULL_DATA) + crp->Kdata->Reset(n); + + } // end of ResetNullValues + +/***********************************************************************/ +/* ODBCColumns: constructs the result blocks containing all columns */ +/* of an ODBC table that will be retrieved by GetData commands. */ +/* Note: The first two columns (Qualifier, Owner) are ignored. */ +/***********************************************************************/ +PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, + char *colpat, bool info) + { + static int dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, + DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT, + DB_SHORT, DB_SHORT, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT, + TYPE_SHORT, TYPE_SHORT, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_TABNAME, + FLD_NAME, FLD_TYPE, FLD_TYPENAME, + FLD_PREC, FLD_LENGTH, FLD_SCALE, + FLD_RADIX, FLD_NULL, FLD_REM}; + static unsigned int length[] = {0, 0, 0, 0, 6, 20, 10, 10, 6, 6, 6, 128}; + int n, ncol = 12; + int maxres; + PQRYRES qrp; + CATPARM *cap; + ODBConn *ocp; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + // We fix a MySQL limit because some data sources return 32767 + n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); + maxres = (n) ? min(n, 4096) : 4096; + n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); + length[0] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); + length[1] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); + length[2] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); + length[3] = (n) ? (n + 1) : 128; + } else { // Info table + maxres = 0; + length[0] = 128; + length[1] = 128; + length[2] = 128; + length[3] = 128; + } // endif ocp + + if (trace) + htrc("ODBCColumns: max=%d len=%d,%d,%d\n", + maxres, length[0], length[1], length[2], length[3]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_COLUMNS, + dbtype, buftyp, fldtyp, length, true, true); + + if (info) // Info table + return qrp; + + if (trace) + htrc("Getting col results ncol=%d\n", qrp->Nbcol); + + cap = AllocCatInfo(g, CAT_COL, table, qrp); + cap->Pat = (PUCHAR)colpat; + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = ocp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + ResetNullValues(cap); + + if (trace) + htrc("Columns: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCColumns + +#if 0 +/**************************************************************************/ +/* MyODBCCols: returns column info as required by ha_connect::pre_create. */ +/**************************************************************************/ +PQRYRES MyODBCCols(PGLOBAL g, char *dsn, char *tab, bool info) + { +// int i, type, len, prec; +// PCOLRES crp, crpt, crpl, crpp; + PQRYRES qrp; + ODBConn *ocp; + + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + if (!info) { + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + } else + ocp = NULL; + + /**********************************************************************/ + /* Get the information about the ODBC table columns. */ + /**********************************************************************/ + if ((qrp = ODBCColumns(g, ocp, dsn, tab, NULL)) && ocp) + dsn = ocp->GetConnect(); // Complete connect string + + /************************************************************************/ + /* Close the local connection. */ + /************************************************************************/ + if (ocp) + ocp->Close(); + + if (!qrp) + return NULL; // Error in ODBCColumns + + /************************************************************************/ + /* Keep only the info used by ha_connect::pre_create. */ + /************************************************************************/ + qrp->Colresp = qrp->Colresp->Next->Next; // Skip Owner and Table names + + crpt = qrp->Colresp->Next; // SQL type + crpl = crpt->Next->Next; // Length + crpp = crpl->Next->Next; // Decimals + + for (int i = 0; i < qrp->Nblin; i++) { + // Types must be PLG types, not SQL types + type = crpt->Kdata->GetIntValue(i); + len = crpl->Kdata->GetIntValue(i); + prec = crpp->Kdata->GetIntValue(i); + type = TranslateSQLType(type, prec, len); + crpt->Kdata->SetValue(type, i); + + // Some data sources do not count prec in length + if (type == TYPE_FLOAT) + len += (prec + 2); // To be safe + + // Could have been changed for blobs or numeric + crpl->Kdata->SetValue(len, i); + } // endfor i + + crpp->Next = crpp->Next->Next->Next; // Should be Remark + + // Renumber crp's for flag comparison + for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) + crp->Ncol = ++i; + + qrp->Nbcol = i; // Should be 7; was 11, skipped 4 + return qrp; + } // end of MyODBCCols +#endif // 0 + +/*************************************************************************/ +/* ODBCDataSources: constructs the result blocks containing all ODBC */ +/* data sources available on the local host. */ +/* Called with info=true to have result column names. */ +/*************************************************************************/ +PQRYRES ODBCDataSources(PGLOBAL g, bool info) + { + static int dbtype[] = {DB_CHAR, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_NAME, FLD_REM}; + static unsigned int length[] = {0, 256}; + int n = 0, ncol = 2; + int maxres; + PQRYRES qrp; + ODBConn *ocp = NULL; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + ocp = new(g) ODBConn(g, NULL); + n = ocp->GetMaxValue(SQL_MAX_DSN_LENGTH); + length[0] = (n) ? (n + 1) : 256; + maxres = 512; // Estimated max number of data sources + } else { + length[0] = 256; + maxres = 0; + } // endif info + + if (trace) + htrc("ODBCDataSources: max=%d len=%d\n", maxres, length[0]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_DSRC, + dbtype, buftyp, fldtyp, length, true, true); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if (!info && ocp->GetDataSources(qrp)) + qrp = NULL; + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCDataSources + +/*************************************************************************/ +/* ODBCDrivers: constructs the result blocks containing all ODBC */ +/* drivers available on the local host. */ +/* Called with info=true to have result column names. */ +/*************************************************************************/ +PQRYRES ODBCDrivers(PGLOBAL g, bool info) + { + static int dbtype[] = {DB_CHAR, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_NAME, FLD_REM}; + static unsigned int length[] = {128, 256}; + int ncol = 2; + int maxres; + PQRYRES qrp; + ODBConn *ocp = NULL; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + ocp = new(g) ODBConn(g, NULL); + maxres = 256; // Estimated max number of drivers + } else + maxres = 0; + + if (trace) + htrc("ODBCDrivers: max=%d len=%d\n", maxres, length[0]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_DRIVER, + dbtype, buftyp, fldtyp, length, true, true); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if (!info && ocp->GetDrivers(qrp)) + qrp = NULL; + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCDrivers + +/***********************************************************************/ +/* ODBCTables: constructs the result blocks containing all tables in */ +/* an ODBC database that will be retrieved by GetData commands. */ +/* Note: The first two columns (Qualifier, Owner) are ignored. */ +/***********************************************************************/ +PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info) + { + static int dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, DB_CHAR, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_STRING, TYPE_STRING}; + static XFLD fldtyp[] = {FLD_QUALIF, FLD_OWNER, FLD_NAME, + FLD_TYPE, FLD_REM}; + static unsigned int length[] = {0, 0, 0, 16, 128}; + int n, ncol = 5; + int maxres; + PQRYRES qrp; + CATPARM *cap; + ODBConn *ocp; + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + if (!info) { + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + maxres = 512; // This is completely arbitrary + n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); + length[0] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); + length[1] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); + length[2] = (n) ? (n + 1) : 128; + } else { + maxres = 0; + length[0] = 128; + length[1] = 128; + length[2] = 128; + } // endif info + + if (trace) + htrc("ODBCTables: max=%d len=%d,%d\n", maxres, length[0], length[1]); + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_TABLES, dbtype, buftyp, + fldtyp, length, true, true); + + if (info) + return qrp; + + cap = AllocCatInfo(g, CAT_TAB, tabpat, qrp); +//cap->Pat = (PUCHAR)tabtyp; + + if (trace) + htrc("Getting table results ncol=%d\n", cap->Qrp->Nbcol); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = ocp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + ResetNullValues(cap); + + if (trace) + htrc("Tables: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /************************************************************************/ + /* Close any local connection. */ + /************************************************************************/ + ocp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCTables + +#if 0 // Currently not used by CONNECT +/**************************************************************************/ +/* PrimaryKeys: constructs the result blocks containing all the */ +/* ODBC catalog information concerning primary keys. */ +/**************************************************************************/ +PQRYRES ODBCPrimaryKeys(PGLOBAL g, ODBConn *op, char *dsn, char *table) + { + static int dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, + DB_CHAR, DB_SHORT, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, + TYPE_STRING, TYPE_SHORT, TYPE_STRING}; + static unsigned int length[] = {0, 0, 0, 0, 6, 128}; + int n, ncol = 5; + int maxres; + PQRYRES qrp; + CATPARM *cap; + ODBConn *ocp = op; + + if (!op) { + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + } // endif op + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + n = ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_TABLE); + maxres = (n) ? (int)n : 250; + n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); + length[0] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); + length[1] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); + length[2] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); + length[3] = (n) ? (n + 1) : 128; + + if (trace) + htrc("ODBCPrimaryKeys: max=%d len=%d,%d,%d\n", + maxres, length[0], length[1], length[2]); + + /************************************************************************/ + /* Allocate the structure used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_PKEY, + dbtype, buftyp, NULL, length, true, true); + + if (trace) + htrc("Getting pkey results ncol=%d\n", qrp->Nbcol); + + cap = AllocCatInfo(g, CAT_KEY, table, qrp); + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = ocp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + ResetNullValues(cap); + + if (trace) + htrc("PrimaryKeys: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /************************************************************************/ + /* Close any local connection. */ + /************************************************************************/ + if (!op) + ocp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of ODBCPrimaryKeys + +/**************************************************************************/ +/* Statistics: constructs the result blocks containing statistics */ +/* about one or several tables to be retrieved by GetData commands. */ +/**************************************************************************/ +PQRYRES ODBCStatistics(PGLOBAL g, ODBConn *op, char *dsn, char *pat, + int un, int acc) + { + static int dbtype[] = {DB_CHAR, DB_CHAR, DB_CHAR, DB_SHORT, DB_CHAR, + DB_CHAR, DB_SHORT, DB_SHORT, DB_CHAR, DB_CHAR, + DB_INT, DB_INT, DB_CHAR}; + static int buftyp[] = {TYPE_STRING, + TYPE_STRING, TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_STRING, TYPE_SHORT, TYPE_SHORT, TYPE_STRING, + TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_STRING}; + static unsigned int length[] = {0, 0, 0 ,6 ,0 ,0 ,6 ,6 ,0 ,2 ,10 ,10 ,128}; + int n, ncol = 13; + int maxres; + PQRYRES qrp; + CATPARM *cap; + ODBConn *ocp = op; + + if (!op) { + /**********************************************************************/ + /* Open the connection with the ODBC data source. */ + /**********************************************************************/ + ocp = new(g) ODBConn(g, NULL); + + if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + return NULL; + + } // endif op + + /************************************************************************/ + /* Do an evaluation of the result size. */ + /************************************************************************/ + n = 1 + ocp->GetMaxValue(SQL_MAX_COLUMNS_IN_INDEX); + maxres = (n) ? (int)n : 32; + n = ocp->GetMaxValue(SQL_MAX_USER_NAME_LEN); + length[1] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_TABLE_NAME_LEN); + length[2] = length[5] = (n) ? (n + 1) : 128; + n = ocp->GetMaxValue(SQL_MAX_QUALIFIER_NAME_LEN); + length[0] = length[4] = (n) ? (n + 1) : length[2]; + n = ocp->GetMaxValue(SQL_MAX_COLUMN_NAME_LEN); + length[7] = (n) ? (n + 1) : 128; + + if (trace) + htrc("SemStatistics: max=%d pat=%s\n", maxres, SVP(pat)); + + /************************************************************************/ + /* Allocate the structure used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, ncol, maxres, IDS_STAT, + dbtype, buftyp, NULL, length, true, true); + + if (trace) + htrc("Getting stat results ncol=%d\n", qrp->Nbcol); + + cap = AllocCatInfo(g, CAT_STAT, pat, qrp); + cap->Unique = (un < 0) ? SQL_INDEX_UNIQUE : (UWORD)un; + cap->Accuracy = (acc < 0) ? SQL_QUICK : (UWORD)acc; + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + if ((n = ocp->GetCatInfo(cap)) >= 0) { + qrp->Nblin = n; + ResetNullValues(cap); + + if (trace) + htrc("Statistics: NBCOL=%d NBLIN=%d\n", qrp->Nbcol, qrp->Nblin); + + } else + qrp = NULL; + + /************************************************************************/ + /* Close any local connection. */ + /************************************************************************/ + if (!op) + ocp->Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of Statistics +#endif // 0 + +/***********************************************************************/ +/* Implementation of DBX class. */ +/***********************************************************************/ +DBX::DBX(RETCODE rc, PSZ msg) + { + m_RC = rc; + m_Msg = msg; + + for (int i = 0; i < MAX_NUM_OF_MSG; i++) + m_ErrMsg[i] = NULL; + + } // end of DBX constructor + +/***********************************************************************/ +/* This function is called by ThrowDBX. */ +/***********************************************************************/ +void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt) + { + if (pdb) { + SWORD len; + RETCODE rc; + UCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1]; + UCHAR state[SQL_SQLSTATE_SIZE + 1]; + SDWORD native; + PGLOBAL g = pdb->m_G; + + rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state, + &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len); + + if (rc != SQL_INVALID_HANDLE) { + // Skip non-errors + for (int i = 0; i < MAX_NUM_OF_MSG + && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) + && strcmp((char*)state, "00000"); i++) { + m_ErrMsg[i] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1); + strcpy(m_ErrMsg[i], (char*)msg); + + if (trace) + htrc("%s: %s, Native=%d\n", state, msg, native); + + rc = SQLError(pdb->m_henv, pdb->m_hdbc, hstmt, state, + &native, msg, SQL_MAX_MESSAGE_LENGTH - 1, &len); + + } // endfor i + + return; + } else { + snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg, + MSG(BAD_HANDLE_VAL)); + m_ErrMsg[0] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1); + strcpy(m_ErrMsg[0], (char*)msg); + + if (trace) + htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC); + + return; + } // endif rc + + } else + m_ErrMsg[0] = "No connexion address provided"; + + if (trace) + htrc("%s: rc=%hd (%s)\n", SVP(m_Msg), m_RC, SVP(m_ErrMsg[0])); + + } // end of BuildErrorMessage + +/***********************************************************************/ +/* ODBConn construction/destruction. */ +/***********************************************************************/ +ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp) + { + m_G = g; + m_Tdb = tdbp; + m_henv = SQL_NULL_HENV; + m_hdbc = SQL_NULL_HDBC; +//m_Recset = NULL + m_hstmt = SQL_NULL_HSTMT; + m_LoginTimeout = DEFAULT_LOGIN_TIMEOUT; + m_QueryTimeout = DEFAULT_QUERY_TIMEOUT; + m_UpdateOptions = 0; + m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10); + m_Catver = (tdbp) ? tdbp->Catver : 0; + m_Connect = NULL; + m_Updatable = true; +//m_Transactions = false; + m_IDQuoteChar = '\''; +//*m_ErrMsg = '\0'; + } // end of ODBConn + +//ODBConn::~ODBConn() +// { +//if (Connected()) +// EndCom(); + +// } // end of ~ODBConn + +/***********************************************************************/ +/* Screen for errors. */ +/***********************************************************************/ +bool ODBConn::Check(RETCODE rc) + { + switch (rc) { + case SQL_SUCCESS_WITH_INFO: + if (trace > 1) { + DBX x(rc); + + x.BuildErrorMessage(this, m_hstmt); + htrc("ODBC Success With Info, hstmt=%p %s\n", + m_hstmt, x.GetErrorMessage(0)); + } // endif trace + + // Fall through + case SQL_SUCCESS: + case SQL_NO_DATA_FOUND: + return true; + } // endswitch rc + + return false; + } // end of Check + +/***********************************************************************/ +/* DB exception throw routines. */ +/***********************************************************************/ +void ODBConn::ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt) + { + DBX* xp = new(m_G) DBX(rc, msg); + + xp->BuildErrorMessage(this, hstmt); + throw xp; + } // end of ThrowDBX + +void ODBConn::ThrowDBX(PSZ msg) + { + DBX* xp = new(m_G) DBX(0, msg); + + xp->m_ErrMsg[0] = msg; + throw xp; + } // end of ThrowDBX + +/***********************************************************************/ +/* Utility routine. */ +/***********************************************************************/ +PSZ ODBConn::GetStringInfo(ushort infotype) + { +//ASSERT(m_hdbc != SQL_NULL_HDBC); + char *p, buffer[MAX_STRING_INFO]; + SWORD result; + RETCODE rc; + + rc = SQLGetInfo(m_hdbc, infotype, buffer, sizeof(buffer), &result); + + if (!Check(rc)) { + ThrowDBX(rc, "SQLGetInfo"); // Temporary +// *buffer = '\0'; + } // endif rc + + p = (char *)PlugSubAlloc(m_G, NULL, strlen(buffer) + 1); + strcpy(p, buffer); + return p; + } // end of GetStringInfo + +/***********************************************************************/ +/* Utility routine. */ +/***********************************************************************/ +int ODBConn::GetMaxValue(ushort infotype) + { +//ASSERT(m_hdbc != SQL_NULL_HDBC); + ushort maxval; + RETCODE rc; + + rc = SQLGetInfo(m_hdbc, infotype, &maxval, 0, NULL); + + if (!Check(rc)) + maxval = 0; + + return (int)maxval; + } // end of GetMaxValue + +/***********************************************************************/ +/* Utility routines. */ +/***********************************************************************/ +void ODBConn::OnSetOptions(HSTMT hstmt) + { + RETCODE rc; + ASSERT(m_hdbc != SQL_NULL_HDBC); + + if ((signed)m_QueryTimeout != -1) { + // Attempt to set query timeout. Ignore failure + rc = SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT, m_QueryTimeout); + + if (!Check(rc)) + // don't attempt it again + m_QueryTimeout = (DWORD)-1; + + } // endif m_QueryTimeout + + if (m_RowsetSize > 0) { + // Attempt to set rowset size. + // In case of failure reset it to 0 to use Fetch. + rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize); + + if (!Check(rc)) + // don't attempt it again + m_RowsetSize = 0; + + } // endif m_RowsetSize + + } // end of OnSetOptions + +/***********************************************************************/ +/* Open: connect to a data source. */ +/***********************************************************************/ +int ODBConn::Open(PSZ ConnectString, DWORD options) + { + PGLOBAL& g = m_G; +//ASSERT_VALID(this); +//ASSERT(ConnectString == NULL || AfxIsValidString(ConnectString)); + ASSERT(!(options & noOdbcDialog && options & forceOdbcDialog)); + + m_Updatable = !(options & openReadOnly); + m_Connect = ConnectString; + + // Allocate the HDBC and make connection + try { + PSZ ver; + + AllocConnect(options); + ver = GetStringInfo(SQL_ODBC_VER); + + if (Connect(options)) { + strcpy(g->Message, MSG(CONNECT_CANCEL)); + return 0; + } // endif + + ver = GetStringInfo(SQL_DRIVER_ODBC_VER); + } catch(DBX *xp) { +// strcpy(g->Message, xp->m_ErrMsg[0]); + strcpy(g->Message, xp->GetErrorMessage(0)); + Close(); +// Free(); + return -1; + } // end try-catch + + // Verify support for required functionality and cache info + VerifyConnect(); + GetConnectInfo(); + return 1; + } // end of Open + +/***********************************************************************/ +/* Allocate an henv (first time called) and hdbc. */ +/***********************************************************************/ +void ODBConn::AllocConnect(DWORD Options) + { + if (m_hdbc != SQL_NULL_HDBC) + return; + + RETCODE rc; +//AfxLockGlobals(CRIT_ODBC); + + // Need to allocate an environment for first connection + if (m_henv == SQL_NULL_HENV) { +// ASSERT(m_nAlloc == 0); + + rc = SQLAllocEnv(&m_henv); + + if (!Check(rc)) { +// AfxUnlockGlobals(CRIT_ODBC); + ThrowDBX(rc, "SQLAllocEnv"); // Fatal + } // endif rc + + } // endif m_henv + + // Do the real thing, allocating connection data + rc = SQLAllocConnect(m_henv, &m_hdbc); + + if (!Check(rc)) { +// AfxUnlockGlobals(CRIT_ODBC); + ThrowDBX(rc, "SQLAllocConnect"); // Fatal + } // endif rc + +//m_nAlloc++; // allocated at last +//AfxUnlockGlobals(CRIT_ODBC); + +#if defined(_DEBUG) + if (Options & traceSQL) { + SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE, (DWORD)"xodbc.out"); + SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1); + } // endif +#endif // _DEBUG + + rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout); + + if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) + htrc("Warning: Failure setting login timeout\n"); + + if (!m_Updatable) { + rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY); + + if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) + htrc("Warning: Failure setting read only access mode\n"); + + } // endif + + // Turn on cursor lib support + if (Options & useCursorLib) + rc = SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC); + + return; + } // end of AllocConnect + +/***********************************************************************/ +/* Connect to data source using SQLDriverConnect. */ +/***********************************************************************/ +bool ODBConn::Connect(DWORD Options) + { + RETCODE rc; + SWORD nResult; + PUCHAR ConnOut = (PUCHAR)PlugSubAlloc(m_G, NULL, MAX_CONNECT_LEN); + UWORD wConnectOption = SQL_DRIVER_COMPLETE; +#if defined(WIN32) + HWND hWndTop = GetForegroundWindow(); + HWND hWnd = GetParent(hWndTop); + + if (hWnd == NULL) + hWnd = GetDesktopWindow(); +#else // !WIN32 + HWND hWnd = NULL; +#endif // !WIN32 + PGLOBAL& g = m_G; + PDBUSER dup = PlgGetUser(g); + + if (Options & noOdbcDialog || dup->Remote) + wConnectOption = SQL_DRIVER_NOPROMPT; + else if (Options & forceOdbcDialog) + wConnectOption = SQL_DRIVER_PROMPT; + + rc = SQLDriverConnect(m_hdbc, hWnd, (PUCHAR)m_Connect, + SQL_NTS, ConnOut, MAX_CONNECT_LEN, + &nResult, wConnectOption); + +#if defined(WIN32) + if (hWndTop) + EnableWindow(hWndTop, true); +#endif // WIN32 + + // If user hit 'Cancel' + if (rc == SQL_NO_DATA_FOUND) { + Close(); +// Free(); + return true; + } // endif rc + + if (!Check(rc)) + ThrowDBX(rc, "SQLDriverConnect"); + + // Save connect string returned from ODBC + m_Connect = (PSZ)ConnOut; + + // All done + return false; + } // end of Connect + +void ODBConn::VerifyConnect() + { +#if defined(NEWMSG) || defined(XMSG) + PGLOBAL& g = m_G; +#endif // NEWMSG || XMSG + RETCODE rc; + SWORD result; + SWORD conformance; + + rc = SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE, + &conformance, sizeof(conformance), &result); + + if (!Check(rc)) + ThrowDBX(rc, "SQLGetInfo"); + + if (conformance < SQL_OAC_LEVEL1) + ThrowDBX(MSG(API_CONF_ERROR)); + + rc = SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE, + &conformance, sizeof(conformance), &result); + + if (!Check(rc)) + ThrowDBX(rc, "SQLGetInfo"); + + if (conformance < SQL_OSC_MINIMUM) + ThrowDBX(MSG(SQL_CONF_ERROR)); + + } // end of VerifyConnect + +void ODBConn::GetConnectInfo() + { + RETCODE rc; + SWORD nResult; +#if 0 // Update not implemented yet + UDWORD DrvPosOp; + + // Reset the database update options + m_UpdateOptions = 0; + + // Check for SQLSetPos support + rc = SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS, + &DrvPosOp, sizeof(DrvPosOp), &nResult); + + if (Check(rc) && + (DrvPosOp & SQL_POS_UPDATE) && + (DrvPosOp & SQL_POS_DELETE) && + (DrvPosOp & SQL_POS_ADD)) + m_UpdateOptions = SQL_SETPOSUPDATES; + + // Check for positioned update SQL support + UDWORD PosStatements; + + rc = SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS, + &PosStatements, sizeof(PosStatements), + &nResult); + + if (Check(rc) && + (PosStatements & SQL_PS_POSITIONED_DELETE) && + (PosStatements & SQL_PS_POSITIONED_UPDATE)) + m_UpdateOptions |= SQL_POSITIONEDSQL; + + if (m_Updatable) { + // Make sure data source is Updatable + char ReadOnly[10]; + + rc = SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY, + ReadOnly, sizeof(ReadOnly), &nResult); + + if (Check(rc) && nResult == 1) + m_Updatable = !!strcmp(ReadOnly, "Y"); + else + m_Updatable = false; + + if (trace) + htrc("Warning: data source is readonly\n"); + + } else // Make data source is !Updatable + rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, + SQL_MODE_READ_ONLY); +#endif // 0 + + // Cache the quote char to use when constructing SQL + char QuoteChar[2]; + + rc = SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR, + QuoteChar, sizeof(QuoteChar), &nResult); + + if (Check(rc) && nResult == 1) + m_IDQuoteChar = QuoteChar[0]; + else + m_IDQuoteChar = ' '; + + if (trace) + htrc("DBMS: %s, Version: %s", + GetStringInfo(SQL_DBMS_NAME), GetStringInfo(SQL_DBMS_VER)); + + } // end of GetConnectInfo + +/***********************************************************************/ +/* Allocate record set and execute an SQL query. */ +/***********************************************************************/ +int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols) + { + PGLOBAL& g = m_G; + void *buffer; + bool b; + UWORD n; + SWORD ncol, len, tp; + SQLLEN afrw; + ODBCCOL *colp; + RETCODE rc; + HSTMT hstmt; + +//m_Recset = new(m_G) RECSET(this); +//ASSERT(m_Recset); + + try { + b = false; + + if (m_hstmt) { + RETCODE rc; + +// All this did not seems to make sense and was been commented out +// if (IsOpen()) +// Close(SQL_CLOSE); + + rc = SQLFreeStmt(m_hstmt, SQL_CLOSE); + hstmt = m_hstmt; + m_hstmt = NULL; + ThrowDBX(MSG(SEQUENCE_ERROR)); + } else { + rc = SQLAllocStmt(m_hdbc, &hstmt); + + if (!Check(rc)) + ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt"); + + } // endif hstmt + + OnSetOptions(hstmt); + b = true; + + if (trace) + htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql); + + do { + rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLExecDirect", hstmt); + + do { + rc = SQLNumResultCols(hstmt, &ncol); + } while (rc == SQL_STILL_EXECUTING); + + if (ncol == 0) { + // Update or Delete statement + rc = SQLRowCount(hstmt, &afrw); + + if (!Check(rc)) + ThrowDBX(rc, "SQLRowCount", hstmt); + + return afrw; + } // endif ncol + + for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext()) + if (!colp->IsSpecial()) + n++; + + // n can be 0 for query such as Select count(*) from table + if (n && n != (UWORD)ncol) + ThrowDBX(MSG(COL_NUM_MISM)); + + // Now bind the column buffers + for (n = 1, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext()) + if (!colp->IsSpecial()) { + buffer = colp->GetBuffer(m_RowsetSize); + len = colp->GetBuflen(); + tp = GetSQLCType(colp->GetResultType()); + + if (tp == SQL_TYPE_NULL) { + sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), + colp->GetResultType(), SVP(colp->GetName())); + ThrowDBX(m_G->Message); + } // endif tp + + if (trace) + htrc("Binding col=%u type=%d buf=%p len=%d slen=%p\n", + n, tp, buffer, len, colp->GetStrLen()); + + rc = SQLBindCol(hstmt, n, tp, buffer, len, colp->GetStrLen()); + + if (!Check(rc)) + ThrowDBX(rc, "SQLBindCol", hstmt); + + n++; + } // endif pcol + + } catch(DBX *x) { + if (trace) + for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) + htrc(x->m_ErrMsg[i]); + + strcpy(m_G->Message, x->GetErrorMessage(0)); + + if (b) + SQLCancel(hstmt); + + rc = SQLFreeStmt(hstmt, SQL_DROP); + m_hstmt = NULL; + return -1; + } // end try/catch + + m_hstmt = hstmt; + return (int)m_RowsetSize; // May have been reset in OnSetOptions + } // end of ExecDirectSQL + +/***********************************************************************/ +/* Get the number of lines of the result set. */ +/***********************************************************************/ +int ODBConn::GetResultSize(char *sql, ODBCCOL *colp) + { + int n = 0; + RETCODE rc; + + if (ExecDirectSQL(sql, colp) < 0) + return -1; + + try { + for (n = 0; ; n++) { + do { + rc = SQLFetch(m_hstmt); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLFetch", m_hstmt); + + if (rc == SQL_NO_DATA_FOUND) + break; + + } // endfor n + + } catch(DBX *x) { +// strcpy(m_G->Message, x->m_ErrMsg[0]); + strcpy(m_G->Message, x->GetErrorMessage(0)); + + if (trace) + for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) + htrc(x->m_ErrMsg[i]); + + SQLCancel(m_hstmt); + n = -2; + } // end try/catch + + rc = SQLFreeStmt(m_hstmt, SQL_DROP); + m_hstmt = NULL; + + if (n != 1) + return -3; + else + return colp->GetIntValue(); + + } // end of GetResultSize + +/***********************************************************************/ +/* Fetch next row. */ +/***********************************************************************/ +int ODBConn::Fetch() + { + ASSERT(m_hstmt); + int irc; + SQLULEN crow; + RETCODE rc; + PGLOBAL& g = m_G; + + try { +// do { + if (m_RowsetSize) { + rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL); + } else { + rc = SQLFetch(m_hstmt); + crow = 1; + } // endif m_RowsetSize +// } while (rc == SQL_STILL_EXECUTING); + + if (trace > 1) + htrc("Fetch: hstmt=%p RowseSize=%d rc=%d\n", + m_hstmt, m_RowsetSize, rc); + + if (!Check(rc)) + ThrowDBX(rc, "Fetch", m_hstmt); + + irc = (rc == SQL_NO_DATA_FOUND) ? 0 : (int)crow; + } catch(DBX *x) { + if (trace) + for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) + htrc(x->m_ErrMsg[i]); + + strcpy(g->Message, x->GetErrorMessage(0)); + irc = -1; + } // end try/catch + + return irc; + } // end of Fetch + +/***********************************************************************/ +/* Prepare an SQL statement for insert. */ +/***********************************************************************/ +int ODBConn::PrepareSQL(char *sql) + { + PGLOBAL& g = m_G; + bool b; + SWORD nparm; + RETCODE rc; + HSTMT hstmt; + + try { + b = false; + + if (m_hstmt) { + RETCODE rc = SQLFreeStmt(m_hstmt, SQL_CLOSE); + + hstmt = m_hstmt; + m_hstmt = NULL; + ThrowDBX(MSG(SEQUENCE_ERROR)); + } else { + rc = SQLAllocStmt(m_hdbc, &hstmt); + + if (!Check(rc)) + ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt"); + + } // endif hstmt + + OnSetOptions(hstmt); + b = true; + + if (trace) + htrc("Prepare hstmt=%p %.64s\n", hstmt, sql); + + do { + rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLPrepare", hstmt); + + do { + rc = SQLNumParams(hstmt, &nparm); + } while (rc == SQL_STILL_EXECUTING); + + } catch(DBX *x) { + if (trace) + for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) + htrc(x->m_ErrMsg[i]); + + strcpy(m_G->Message, x->GetErrorMessage(0)); + + if (b) + SQLCancel(hstmt); + + rc = SQLFreeStmt(hstmt, SQL_DROP); + m_hstmt = NULL; + return -1; + } // end try/catch + + m_hstmt = hstmt; + return (int)nparm; + } // end of PrepareSQL + +/***********************************************************************/ +/* Bind a parameter for inserting. */ +/***********************************************************************/ +bool ODBConn::ExecuteSQL(void) + { + RETCODE rc; + + try { + rc = SQLExecute(m_hstmt); + + if (!Check(rc)) + ThrowDBX(rc, "SQLExecute", m_hstmt); + + } catch(DBX *x) { + strcpy(m_G->Message, x->GetErrorMessage(0)); + SQLCancel(m_hstmt); + rc = SQLFreeStmt(m_hstmt, SQL_DROP); + m_hstmt = NULL; + return true; + } // end try/catch + + return false; + } // end of ExecuteSQL + +/***********************************************************************/ +/* Bind a parameter for inserting. */ +/***********************************************************************/ +bool ODBConn::BindParam(ODBCCOL *colp) + { + void *buf; + UWORD n = colp->GetRank(); + SWORD ct, sqlt; + UDWORD len; + SQLLEN *strlen = colp->GetStrLen(); + RETCODE rc; + +#if 0 + try { + SWORD dec, nul; + rc = SQLDescribeParam(m_hstmt, n, &sqlt, &len, &dec, &nul); + + if (!Check(rc)) + ThrowDBX(rc, m_hstmt); + + } catch(DBX *x) { + strcpy(m_G->Message, x->GetErrorMessage(0)); + } // end try/catch +#endif // 0 + + buf = colp->GetBuffer(0); +// len = colp->GetBuflen(); + len = IsTypeNum(colp->GetResultType()) ? 0 : colp->GetBuflen(); + ct = GetSQLCType(colp->GetResultType()); + sqlt = GetSQLType(colp->GetResultType()); + *strlen = IsTypeNum(colp->GetResultType()) ? 0 : SQL_NTS; + + try { + rc = SQLBindParameter(m_hstmt, n, SQL_PARAM_INPUT, ct, sqlt, + len, 0, buf, 0, strlen); + + if (!Check(rc)) + ThrowDBX(rc, "SQLBindParameter", m_hstmt); + + } catch(DBX *x) { + strcpy(m_G->Message, x->GetErrorMessage(0)); + SQLCancel(m_hstmt); + rc = SQLFreeStmt(m_hstmt, SQL_DROP); + m_hstmt = NULL; + return true; + } // end try/catch + + return false; + } // end of BindParam + +/***********************************************************************/ +/* Get the list of Data Sources and set it in qrp. */ +/***********************************************************************/ +bool ODBConn::GetDataSources(PQRYRES qrp) + { + bool rv = false; + UCHAR *dsn, *des; + UWORD dir = SQL_FETCH_FIRST; + SWORD n1, n2, p1, p2; + PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next; + RETCODE rc; + + n1 = crp1->Clen; + n2 = crp2->Clen; + + try { + rc = SQLAllocEnv(&m_henv); + + if (!Check(rc)) + ThrowDBX(rc, "SQLAllocEnv"); // Fatal + + for (int i = 0; i < qrp->Maxres; i++) { + dsn = (UCHAR*)crp1->Kdata->GetValPtr(i); + des = (UCHAR*)crp2->Kdata->GetValPtr(i); + rc = SQLDataSources(m_henv, dir, dsn, n1, &p1, des, n2, &p2); + + if (rc == SQL_NO_DATA_FOUND) + break; + else if (!Check(rc)) + ThrowDBX(rc, "SQLDataSources"); + + qrp->Nblin++; + dir = SQL_FETCH_NEXT; + } // endfor i + + } catch(DBX *x) { + strcpy(m_G->Message, x->GetErrorMessage(0)); + rv = true; + } // end try/catch + +//SQLFreeEnv(m_henv); + Close(); + return rv; + } // end of GetDataSources + +/***********************************************************************/ +/* Get the list of Drivers and set it in qrp. */ +/***********************************************************************/ +bool ODBConn::GetDrivers(PQRYRES qrp) + { + int i, n; + bool rv = false; + UCHAR *des, *att; + UWORD dir = SQL_FETCH_FIRST; + SWORD n1, n2, p1, p2; + PCOLRES crp1 = qrp->Colresp, crp2 = qrp->Colresp->Next; + RETCODE rc; + + n1 = crp1->Clen; + n2 = crp2->Clen; + + try { + rc = SQLAllocEnv(&m_henv); + + if (!Check(rc)) + ThrowDBX(rc, "SQLAllocEnv"); // Fatal + + for (n = 0; n < qrp->Maxres; n++) { + des = (UCHAR*)crp1->Kdata->GetValPtr(n); + att = (UCHAR*)crp2->Kdata->GetValPtr(n); + rc = SQLDrivers(m_henv, dir, des, n1, &p1, att, n2, &p2); + + if (rc == SQL_NO_DATA_FOUND) + break; + else if (!Check(rc)) + ThrowDBX(rc, "SQLDrivers"); + + + // The attributes being separated by '\0', set them to ';' + for (i = 0; i < p2; i++) + if (!att[i]) + att[i] = ';'; + + qrp->Nblin++; + dir = SQL_FETCH_NEXT; + } // endfor n + + } catch(DBX *x) { + strcpy(m_G->Message, x->GetErrorMessage(0)); + rv = true; + } // end try/catch + +//SQLFreeEnv(m_henv); + Close(); + return rv; + } // end of GetDrivers + +/***********************************************************************/ +/* Allocate recset and call SQLTables, SQLColumns or SQLPrimaryKeys. */ +/***********************************************************************/ +int ODBConn::GetCatInfo(CATPARM *cap) + { +#if defined(NEWMSG) || defined(XMSG) + PGLOBAL& g = m_G; +#endif // NEWMSG || XMSG + void *buffer; + int i, irc; + bool b; + PSZ fnc = "Unknown"; + UWORD n; + SWORD ncol, len, tp; + SQLULEN crow; + PCOLRES crp; + RETCODE rc; + HSTMT hstmt = NULL; + SQLLEN *vl, *vlen; + PVAL *pval = NULL; + + try { + b = false; + + if (!m_hstmt) { + rc = SQLAllocStmt(m_hdbc, &hstmt); + + if (!Check(rc)) + ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt"); + + } else + ThrowDBX(MSG(SEQUENCE_ERROR)); + + b = true; + + if ((m_RowsetSize = cap->Qrp->Maxres) > 0) { + if (m_Catver) { + // Attempt to set rowset size. + // In case of failure reset it to 0 to use Fetch. + if (m_Catver == 3) // ODBC Ver 3 + rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, + (SQLPOINTER)m_RowsetSize, 0); + else + rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, m_RowsetSize); + + if (!Check(rc)) + m_RowsetSize = 1; // don't attempt it again +// ThrowDBX(rc, hstmt); // Temporary + + if (m_Catver == 3) { // ODBC Ver 3 + rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, cap->Status, 0); + rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &crow, 0); + } // endif m_Catver + + } else // ORABUG + m_RowsetSize = 1; + + } else + ThrowDBX("0-sized result"); + + // Now do call the proper ODBC API + switch (cap->Id) { + case CAT_TAB: +// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, +// (SQLPOINTER)false, 0); + fnc = "SQLTables"; + rc = SQLTables(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, + cap->Pat, SQL_NTS); + break; + case CAT_COL: +// rc = SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, +// (SQLPOINTER)true, 0); + fnc = "SQLColumns"; + rc = SQLColumns(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, + cap->Pat, SQL_NTS); + break; + case CAT_KEY: + fnc = "SQLPrimaryKeys"; + rc = SQLPrimaryKeys(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS); + break; + case CAT_STAT: + fnc = "SQLStatistics"; + rc = SQLStatistics(hstmt, NULL, 0, NULL, 0, cap->Tab, SQL_NTS, + cap->Unique, cap->Accuracy); + break; + case CAT_SPC: + ThrowDBX("SQLSpecialColumns not available yet"); + } // endswitch infotype + + if (!Check(rc)) + ThrowDBX(rc, fnc, hstmt); + + rc = SQLNumResultCols(hstmt, &ncol); + + // n because we no more ignore the first column + if ((n = (UWORD)cap->Qrp->Nbcol) > (UWORD)ncol) + ThrowDBX(MSG(COL_NUM_MISM)); + + if (m_RowsetSize == 1 && cap->Qrp->Maxres > 1) { + pval = (PVAL *)PlugSubAlloc(m_G, NULL, n * sizeof(PVAL)); + vlen = (SQLLEN *)PlugSubAlloc(m_G, NULL, n * sizeof(SDWORD *)); + } // endif + + // Now bind the column buffers + for (n = 0, crp = cap->Qrp->Colresp; crp; crp = crp->Next) { + if (pval) { + pval[n] = AllocateValue(m_G, crp->Kdata->GetType(), + crp->Kdata->GetVlen(), 0); + buffer = pval[n]->GetTo_Val(); + vl = vlen + n; + } else { + buffer = crp->Kdata->GetValPointer(); + vl = cap->Vlen[n]; + } // endif pval + + len = GetTypeSize(crp->Type, crp->Clen); + tp = GetSQLCType(crp->Type); + + if (tp == SQL_TYPE_NULL) { + sprintf(m_G->Message, MSG(INV_COLUMN_TYPE), crp->Type, crp->Name); + ThrowDBX(m_G->Message); + } // endif tp + + // n + 1 because column numbers begin with 1 + rc = SQLBindCol(hstmt, n + 1, tp, buffer, len, vl); + + if (!Check(rc)) + ThrowDBX(rc, "SQLBindCol", hstmt); + + n++; + } // endfor crp + + fnc = "SQLFetch"; + + // Now fetch the result + if (m_Catver != 3) { + if (m_RowsetSize > 1) { + fnc = "SQLExtendedFetch"; + rc = SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &crow, cap->Status); + } else if (pval) { + for (n = 0; n < cap->Qrp->Maxres; n++) { + if ((rc = SQLFetch(hstmt)) != SQL_SUCCESS) + break; + + for (i = 0, crp = cap->Qrp->Colresp; crp; i++, crp = crp->Next) { + crp->Kdata->SetValue(pval[i], n); + cap->Vlen[i][n] = vlen[i]; + } // endfor crp + + } // endfor n + + if ((crow = n) && rc == SQL_NO_DATA) + rc = SQL_SUCCESS; + + } else { + rc = SQLFetch(hstmt); + crow = 1; + } // endif's + + } else // ODBC Ver 3 + rc = SQLFetch(hstmt); + +// if (!Check(rc)) + if (rc == SQL_NO_DATA_FOUND) { + if (cap->Pat) + sprintf(m_G->Message, MSG(NO_TABCOL_DATA), cap->Tab, cap->Pat); + else + sprintf(m_G->Message, MSG(NO_TAB_DATA), cap->Tab); + + ThrowDBX(m_G->Message); + } else if (rc != SQL_SUCCESS) + ThrowDBX(rc, fnc, hstmt); + + irc = (int)crow; + } catch(DBX *x) { + if (trace) + for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++) + htrc(x->m_ErrMsg[i]); + + strcpy(m_G->Message, x->GetErrorMessage(0)); + irc = -1; + } // end try/catch + + if (b) + SQLCancel(hstmt); + + // All this (hstmt vs> m_hstmt) to be revisited + if (hstmt) + rc = SQLFreeStmt(hstmt, SQL_DROP); + + return irc; + } // end of GetCatInfo + +/***********************************************************************/ +/* Disconnect connection */ +/***********************************************************************/ +void ODBConn::Close() + { + RETCODE rc; + +#if 0 + // Close any open recordsets + AfxLockGlobals(CRIT_ODBC); + TRY + { + while (!m_listRecordsets.IsEmpty()) + { + CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead(); + pSet->Close(); // will implicitly remove from list + pSet->m_pDatabase = NULL; + } + } + CATCH_ALL(e) + { + AfxUnlockGlobals(CRIT_ODBC); + THROW_LAST(); + } + END_CATCH_ALL + AfxUnlockGlobals(CRIT_ODBC); +#endif // 0 + + if (m_hstmt) { + // Is required for multiple tables + rc = SQLFreeStmt(m_hstmt, SQL_DROP); + m_hstmt = NULL; + } // endif m_hstmt + + if (m_hdbc != SQL_NULL_HDBC) { + rc = SQLDisconnect(m_hdbc); + rc = SQLFreeConnect(m_hdbc); + m_hdbc = SQL_NULL_HDBC; + +// AfxLockGlobals(CRIT_ODBC); +// ASSERT(m_nAlloc != 0); +// m_nAlloc--; +// AfxUnlockGlobals(CRIT_ODBC); + } // endif m_hdbc + + if (m_henv != SQL_NULL_HENV) { + if (trace) { + RETCODE rc = SQLFreeEnv(m_henv); + + if (rc != SQL_SUCCESS) // Nothing we can do + htrc("Error: SQLFreeEnv failure ignored in Close\n"); + + } else + SQLFreeEnv(m_henv); + + m_henv = SQL_NULL_HENV; + } // endif m_henv + + } // end of Close + +#if 0 +// Silently disconnect and free all ODBC resources. +// Don't throw any exceptions +void ODBConn::Free() + { + // Trap failures upon close + try { + Close(); + } catch(DBX*) { + // Nothing we can do + if (trace) + htrc("Error: exception by Close ignored in Free\n"); + +// DELETE_EXCEPTION(x); + } // endcatch + + // free henv if refcount goes to 0 +//AfxLockGlobals(CRIT_ODBC); + if (m_henv != SQL_NULL_HENV) { + ASSERT(m_nAlloc >= 0); + + if (m_nAlloc == 0) { + // free last connection - release HENV + if (trace) { + RETCODE rc = SQLFreeEnv(m_henv); + + if (rc != SQL_SUCCESS) // Nothing we can do + htrc("Error: SQLFreeEnv failure ignored in Free\n"); + + } else + SQLFreeEnv(m_henv); + + m_henv = SQL_NULL_HENV; + } // endif m_nAlloc + } // endif m_henv +//AfxUnlockGlobals(CRIT_ODBC); + } // end of Free + +////////////////////////////////////////////////////////////////////////////// +// CRecordset helpers + +//id AFXAPI AfxSetCurrentRecord(int* plCurrentRecord, int nRows, RETCODE nRetCode); +//id AFXAPI AfxSetRecordCount(int* plRecordCount, int lCurrentRecord, +//bool bEOFSeen, RETCODE nRetCode); + +/***********************************************************************/ +/* RECSET class implementation */ +/***********************************************************************/ +RECSET::RECSET(ODBConn *dbcp) + { + m_pDB = dbcp; + m_hstmt = SQL_NULL_HSTMT; + m_OpenType = snapshot; + m_Options = none; + +#if 0 + m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN; + m_nEditMode = noMode; + m_nDefaultType = snapshot; + + m_bAppendable = false; + m_bUpdatable = false; + m_bScrollable = false; + m_bRecordsetDb = false; + m_bRebindParams = false; + m_bLongBinaryColumns = false; + m_nLockMode = optimistic; + m_dwInitialGetDataLen = 0; + m_rgODBCFieldInfos = NULL; + m_rgFieldInfos = NULL; + m_rgRowStatus = NULL; + m_dwRowsetSize = 25; + m_dwAllocatedRowsetSize = 0; + + m_nFields = 0; + m_nParams = 0; + m_nFieldsBound = 0; + m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED; + m_lRecordCount = 0; + m_bUseUpdateSQL = false; + m_bUseODBCCursorLib = false; + m_nResultCols = -1; + m_bCheckCacheForDirtyFields = true; + + m_pbFieldFlags = NULL; + m_pbParamFlags = NULL; + m_plParamLength = NULL; + m_pvFieldProxy = NULL; + m_pvParamProxy = NULL; + m_nProxyFields = 0; + m_nProxyParams = 0; + + m_hstmtUpdate = SQL_NULL_HSTMT; +#endif // 0 + } // end of RECSET constructor + +RECSET::~RECSET() + { + try { + if (m_hstmt) { + if (trace && (m_dwOptions & useMultiRowFetch)) { + htrc("WARNING: Close called implicitly from destructor\n"); + htrc("Use of multi row fetch requires explicit call\n"); + htrc("to Close or memory leaks will result\n"); + } // endif trace + + Close(); + } // endif m_hstmt + +// if (m_bRecordsetDb) +// delete m_pDB; ?????? + + m_pDB = NULL; + } catch(DBX*) { + // Nothing we can do + if (trace) + htrc("Error: Exception ignored in ~RECSET\n"); + + } // endtry/catch + + } // end of ~RECSET + +/***********************************************************************/ +/* Open: this function does the following: */ +/* Allocates the hstmt, */ +/* Bind columns, */ +/* Execute the SQL statement */ +/***********************************************************************/ +bool RECSET::Open(PSZ sql, uint Type, DWORD options) + { + ASSERT(m_pDB && m_pDB->IsOpen()); + ASSERT(Type == DB_USE_DEFAULT_TYPE || Type == dynaset || + Type == snapshot || Type == forwardOnly || Type == dynamic); +//ASSERT(!(options & readOnly && options & appendOnly)); + + // Cache state info and allocate hstmt + SetState(Type, sql, options); + + try { + if (m_hstmt) { + if (IsOpen()) + Close(SQL_CLOSE); + + } else { + RETCODE rc = SQLAllocStmt(m_pDB->m_hdbc, &m_hstmt); + + if (!Check(rc)) + ThrowDBException(SQL_INVALID_HANDLE); + + } // endif m_hstmt + + m_pDB->OnSetOptions(m_hstmt); + + // Allocate the field/param status arrays, if necessary +// bool bUnbound = false; + +// if (m_nFields > 0 || m_nParams > 0) +// AllocStatusArrays(); +// else +// bUnbound = true; + + // Build SQL and prep/execute or just execute direct +// BuildSQL(sql); + PrepareAndExecute(sql); + + // Cache some field info and prepare the rowset + AllocAndCacheFieldInfo(); + AllocRowset(); + + // If late binding, still need to allocate status arrays +// if (bUnbound && (m_nFields > 0 || m_nParams > 0)) +// AllocStatusArrays(); + + } catch(DBX *x) { + Close(SQL_DROP); +// strcpy(m_pDB->m_G->Message, x->GetErrorMessage[0]); + strcpy(m_pDB->m_G->Message, x->GetErrorMessage(0)); + return true; + } // endtry/catch + + return false; + } // end of Open + +/***********************************************************************/ +/* Close a hstmt. */ +/***********************************************************************/ +void RECSET::Close(SWORD option) + { + if (m_hstmt != SQL_NULL_HSTMT) { + RETCODE rc = SQLFreeStmt(m_hstmt, option); + + if (option == SQL_DROP) + m_hstmt = SQL_NULL_HSTMT; + + } // endif m_hstmt + +#if 0 + m_lOpen = RECORDSET_STATUS_CLOSED; + m_bBOF = true; + m_bEOF = true; + m_bDeleted = false; + m_bAppendable = false; + m_bUpdatable = false; + m_bScrollable = false; + m_bRebindParams = false; + m_bLongBinaryColumns = false; + m_nLockMode = optimistic; + m_nFieldsBound = 0; + m_nResultCols = -1; +#endif // 0 + } // end of Close +#endif // 0 diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h new file mode 100644 index 00000000000..a84cb19b485 --- /dev/null +++ b/storage/connect/odbconn.h @@ -0,0 +1,189 @@ +/***********************************************************************/ +/* ODBConn.h : header file for the ODBC connection classes. */ +/***********************************************************************/ +//nclude <windows.h> /* Windows include file */ +//nclude <windowsx.h> /* Message crackers */ + +/***********************************************************************/ +/* Included C-definition files required by the interface. */ +/***********************************************************************/ +#include "block.h" + +/***********************************************************************/ +/* ODBC interface. */ +/***********************************************************************/ +#include <sql.h> +#include <sqlext.h> + +/***********************************************************************/ +/* Constants and defines. */ +/***********************************************************************/ +// Miscellaneous sizing info +#define MAX_NUM_OF_MSG 10 // Max number of error messages +//efine MAX_CURRENCY 30 // Max size of Currency($) string +#define MAX_TNAME_LEN 32 // Max size of table names +//efine MAX_FNAME_LEN 256 // Max size of field names +#define MAX_STRING_INFO 256 // Max size of string from SQLGetInfo +//efine MAX_DNAME_LEN 256 // Max size of Recordset names +#define MAX_CONNECT_LEN 512 // Max size of Connect string +//efine MAX_CURSOR_NAME 18 // Max size of a cursor name +#define DEFAULT_FIELD_TYPE SQL_TYPE_NULL // pick "C" data type to match SQL data type + +#if !defined(WIN32) +typedef unsigned char *PUCHAR; +#endif // !WIN32 + +// Timeout and net wait defaults +#define DEFAULT_LOGIN_TIMEOUT 15 // seconds to before fail on connect +#define DEFAULT_QUERY_TIMEOUT 15 // seconds to before fail waiting for results + +// Field Flags, used to indicate status of fields +//efine SQL_FIELD_FLAG_DIRTY 0x1 +//efine SQL_FIELD_FLAG_NULL 0x2 + +// Update options flags +#define SQL_SETPOSUPDATES 0x0001 +#define SQL_POSITIONEDSQL 0x0002 +//efine SQL_GDBOUND 0x0004 + +enum CATINFO {CAT_TAB = 1, /* SQLTables */ + CAT_COL = 2, /* SQLColumns */ + CAT_KEY = 3, /* SQLPrimaryKeys */ + CAT_STAT = 4, /* SQLStatistics */ + CAT_SPC = 5}; /* SQLSpecialColumns */ + +/***********************************************************************/ +/* This structure is used to control the catalog functions. */ +/***********************************************************************/ +typedef struct tagCATPARM { + CATINFO Id; // Id to indicate function + PQRYRES Qrp; // Result set pointer + PUCHAR Tab; // Table name or pattern + PUCHAR Pat; // Table type or column pattern + SQLLEN* *Vlen; // To array of indicator values + UWORD *Status; // To status block + // For SQLStatistics + UWORD Unique; // Index type + UWORD Accuracy; // For Cardinality and Pages + // For SQLSpecialColumns + UWORD ColType; + UWORD Scope; + UWORD Nullable; + } CATPARM; + +// ODBC connection to a data source +class TDBODBC; +class ODBCCOL; +class ODBConn; + +/***********************************************************************/ +/* Class DBX (ODBC exception). */ +/***********************************************************************/ +class DBX : public BLOCK { + friend class ODBConn; + // Construction (by ThrowDBX only) -- destruction + protected: + DBX(RETCODE rc, PSZ msg = NULL); + public: +//virtual ~DBX() {} +//void operator delete(void*, PGLOBAL, void*) {}; + + // Implementation (use ThrowDBX to create) + RETCODE GetRC(void) {return m_RC;} + PSZ GetMsg(void) {return m_Msg;} + const char *GetErrorMessage(int i) + {return (i >=0 && i < MAX_NUM_OF_MSG) ? m_ErrMsg[i] : "No ODBC error";} + + protected: + void BuildErrorMessage(ODBConn* pdb, HSTMT hstmt = SQL_NULL_HSTMT); + + // Attributes + RETCODE m_RC; + PSZ m_Msg; + PSZ m_ErrMsg[MAX_NUM_OF_MSG]; + }; // end of DBX class definition + +/***********************************************************************/ +/* ODBConn class. */ +/***********************************************************************/ +class ODBConn : public BLOCK { + friend class DBX; + friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&); + private: + ODBConn(); // Standard (unused) constructor + + public: + ODBConn(PGLOBAL g, TDBODBC *tdbp); + + enum DOP { // Db Open oPtions + traceSQL = 0x0001, // Trace SQL calls + openReadOnly = 0x0002, // Open database read only + useCursorLib = 0x0004, // Use ODBC cursor lib + noOdbcDialog = 0x0008, // Don't display ODBC Connect dialog + forceOdbcDialog = 0x0010}; // Always display ODBC connect dialog + + int Open(PSZ ConnectString, DWORD Options = 0); + void Close(void); + + // Attributes + public: + char GetQuoteChar(void) {return m_IDQuoteChar;} + // Database successfully opened? + bool IsOpen(void) {return m_hdbc != SQL_NULL_HDBC;} + PSZ GetStringInfo(ushort infotype); + int GetMaxValue(ushort infotype); + PSZ GetConnect(void) {return m_Connect;} + + public: + // Operations + void SetLoginTimeout(DWORD sec) {m_LoginTimeout = sec;} + void SetQueryTimeout(DWORD sec) {m_QueryTimeout = sec;} + int GetResultSize(char *sql, ODBCCOL *colp); + int ExecDirectSQL(char *sql, ODBCCOL *tocols); + int Fetch(void); + int PrepareSQL(char *sql); + bool ExecuteSQL(void); + bool BindParam(ODBCCOL *colp); + int GetCatInfo(CATPARM *cap); + bool GetDataSources(PQRYRES qrp); + bool GetDrivers(PQRYRES qrp); + + public: + // Set special options + void OnSetOptions(HSTMT hstmt); + + // Implementation + public: +// virtual ~ODBConn(); + + // ODBC operations + protected: + bool Check(RETCODE rc); + void ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt = SQL_NULL_HSTMT); + void ThrowDBX(PSZ msg); + void AllocConnect(DWORD dwOptions); + bool Connect(DWORD Options); + void VerifyConnect(void); + void GetConnectInfo(void); + void Free(void); + + protected: + // Static members +//static HENV m_henv; +//static int m_nAlloc; // per-Appl reference to HENV above + + // Members + PGLOBAL m_G; + TDBODBC *m_Tdb; + HENV m_henv; + HDBC m_hdbc; + HSTMT m_hstmt; + DWORD m_LoginTimeout; + DWORD m_QueryTimeout; + DWORD m_UpdateOptions; + DWORD m_RowsetSize; + int m_Catver; + PSZ m_Connect; + bool m_Updatable; + char m_IDQuoteChar; + }; // end of ODBConn class definition diff --git a/storage/connect/os.h b/storage/connect/os.h new file mode 100644 index 00000000000..282eab1d9d7 --- /dev/null +++ b/storage/connect/os.h @@ -0,0 +1,60 @@ +#ifndef _OS_H_INCLUDED +#define _OS_H_INCLUDED + +#if defined __FreeBSD__ +typedef off_t off64_t; +#define lseek64(fd, offset, whence) lseek((fd), (offset), (whence)) +#define open64(path, flags, mode) open((path), (flags), (mode)) +#define ftruncate64(fd, length) ftruncate((fd), (length)) +#define O_LARGEFILE 0 +#endif + +#if defined(WIN32) +typedef __int64 BIGINT; +#else // !WIN32 +typedef longlong BIGINT; +#define FILE_BEGIN SEEK_SET +#define FILE_CURRENT SEEK_CUR +#define FILE_END SEEK_END +#endif // !WIN32 + +#if !defined(WIN32) +typedef const void *LPCVOID; +typedef const char *LPCTSTR; +typedef const char *LPCSTR; +typedef unsigned char BYTE; +typedef char *LPSTR; +typedef char *LPTSTR; +typedef char *PSZ; +typedef long BOOL; +typedef int INT; +#if !defined(NODW) +/* + sqltypes.h from unixODBC incorrectly defines + DWORD as "unsigned int" instead of "unsigned long" on 64-bit platforms. + Add "#define NODW" into all files including this file that include + sqltypes.h (through sql.h or sqlext.h). +*/ +typedef unsigned long DWORD; +#endif /* !NODW */ +#undef HANDLE +typedef int HANDLE; + +#define stricmp strcasecmp +#define _stricmp strcasecmp +#define strnicmp strncasecmp +#define _strnicmp strncasecmp +#ifdef PATH_MAX +#define _MAX_PATH PATH_MAX +#else +#define _MAX_PATH 260 +#endif +#define _MAX_DRIVE 3 +#define _MAX_DIR 256 +#define _MAX_FNAME 256 +#define _MAX_EXT 256 +#define INVALID_HANDLE_VALUE (-1) +#define __stdcall +#endif /* !WIN32 */ + +#endif /* _OS_H_INCLUDED */ diff --git a/storage/connect/osutil.c b/storage/connect/osutil.c new file mode 100644 index 00000000000..45c834a4cfc --- /dev/null +++ b/storage/connect/osutil.c @@ -0,0 +1,231 @@ +#include "my_global.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "osutil.h" + +#ifdef WIN32 +my_bool CloseFileHandle(HANDLE h) + { + return !CloseHandle(h); + } /* end of CloseFileHandle */ + +#else /* UNIX */ +/* code to handle Linux and Solaris */ +#include <unistd.h> +#include <sys/stat.h> +#include <ctype.h> +#include <fcntl.h> + +extern FILE *debug; + +/***********************************************************************/ +/* Define some functions not existing in the UNIX library. */ +/***********************************************************************/ +PSZ strupr(PSZ p) + { + register int i; + + for (i = 0; p[i]; i++) + p[i] = toupper(p[i]); + + return (p); + } /* end of strupr */ + +PSZ strlwr(PSZ p) + { + register int i; + + for (i = 0; p[i]; i++) + p[i] = tolower(p[i]); + + return (p); + } /* end of strlwr */ + +#if defined(NOT_USED) /*&& !defined(sun) && !defined(LINUX) && !defined(AIX)*/ +/***********************************************************************/ +/* Define stricmp function not existing in some UNIX libraries. */ +/***********************************************************************/ +int stricmp(char *str1, char *str2) + { + register int i; + int n; + char c; + char *sup1 = malloc(strlen(str1) + 1); + char *sup2 = malloc(strlen(str2) + 1); + + for (i = 0; c = str1[i]; i++) + sup1[i] = toupper(c); + + sup1[i] = 0; + + for (i = 0; c = str2[i]; i++) + sup2[i] = toupper(c); + + sup2[i] = 0; + n = strcmp(sup1, sup2); + free(sup1); + free(sup2); + return (n); + } /* end of stricmp */ +#endif /* sun */ + +/***********************************************************************/ +/* Define the splitpath function not existing in the UNIX library. */ +/***********************************************************************/ +void _splitpath(LPCSTR name, LPSTR drive, LPSTR dir, LPSTR fn, LPSTR ft) + { + LPCSTR p2, p = name; + +#ifdef DEBTRACE + fprintf(debug,"SplitPath: name=%s [%s (%d)]\n", + XSTR(name), XSTR(__FILE__), __LINE__); +#endif + + if (drive) *drive = '\0'; + if (dir) *dir = '\0'; + if (fn) *fn = '\0'; + if (ft) *ft = '\0'; + + if ((p2 = strrchr(p, '/'))) { + p2++; + if (dir) strncat(dir, p, p2 - p); + p = p2; + } /* endif p2 */ + + if ((p2 = strrchr(p, '.'))) { + if (fn) strncat(fn, p, p2 - p); + if (ft) strcpy(ft, p2); + } else + if (fn) strcpy(fn, p); + +#ifdef DEBTRACE + fprintf(debug,"SplitPath: name=%s drive=%s dir=%s filename=%s type=%s [%s(%d)]\n", + XSTR(name), XSTR(drive), XSTR(dir), XSTR(fn), XSTR(ft), __FILE__, __LINE__); +#endif + } /* end of _splitpath */ + +/***********************************************************************/ +/* Define the makepath function not existing in the UNIX library. */ +/***********************************************************************/ +void _makepath(LPSTR name, LPCSTR drive, LPCSTR dir, LPCSTR fn, LPCSTR ft) + { + int n; + + if (!name) + return; + else + *name = '\0'; + + if (dir && (n = strlen(dir)) > 0) { + strcpy(name, dir); + + if (name[n-1] != '/') + strcat(name, "/"); + + } /* endif dir */ + + if (fn) + strcat(name, fn); + + if (ft && strlen(ft)) { + if (*ft != '.') + strcat(name, "."); + + strcat(name, ft); + } /* endif ft */ + + } /* end of _makepath */ + +my_bool CloseFileHandle(HANDLE h) + { + return (close(h)) ? TRUE : FALSE; + } /* end of CloseFileHandle */ + +void Sleep(DWORD time) + { + //FIXME: TODO + } /* end of Sleep */ + +int GetLastError() + { + return errno; + } /* end of GetLastError */ + +unsigned long _filelength(int fd) + { + struct stat st; + + if (fd == -1) + return 0; + + if (fstat(fd, &st) != 0) + return 0; + + return st.st_size; + } /* end of _filelength */ + +char *_fullpath(char *absPath, const char *relPath, size_t maxLength) + { + // Fixme + char *p; + + if( *relPath == '\\' || *relPath == '/' ) { + strncpy(absPath, relPath, maxLength); + } else if(*relPath == '~') { + // get the path to the home directory + // Fixme + strncpy(absPath, relPath, maxLength); + } else { + char buff[2*_MAX_PATH]; + + getcwd(buff, _MAX_PATH); + strcat(buff,"/"); + strcat(buff, relPath); + strncpy(absPath, buff, maxLength); + } /* endif's relPath */ + + p = absPath; + + for(; *p; p++) + if (*p == '\\') + *p = '/'; + + return absPath; + } /* end of _fullpath */ + +BOOL MessageBeep(uint i) + { + // Fixme + return TRUE; + } /* end of MessageBeep */ + +LPSTR _strerror(int errn) + { + static char buff[256]; + + sprintf(buff,"error: %d", errn); + return buff; + } /* end of _strerror */ + +int _isatty(int fileNo) + { + return isatty(fileNo); + } /* end of _isatty */ + +/* This function is ridiculous and should be revisited */ +DWORD FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, + DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, ...) + { + char buff[32]; + int n; + +//if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) +// return 0; /* means error */ + + n = sprintf(buff, "Error code: %d", (int) dwMessageId); + strncpy(lpBuffer, buff, nSize); + return min(n, nSize); + } /* end of FormatMessage */ + +#endif // UNIX diff --git a/storage/connect/osutil.h b/storage/connect/osutil.h new file mode 100644 index 00000000000..440373dd7a1 --- /dev/null +++ b/storage/connect/osutil.h @@ -0,0 +1,101 @@ +#ifndef __OSUTIL_H__ +#define __OSUTIL_H__ + +#if defined(UNIX) || defined(UNIV_LINUX) +#include "my_global.h" +#include <errno.h> +#include <stddef.h> +#include "os.h" + +#define MB_OK 0x00000000 + +#if defined(__cplusplus) +#if !defined(__MINMAX_DEFINED) +#define __MINMAX_DEFINED +#ifndef max +#define max(x,y) (((x)>(y))?(x):(y)) +#endif +#ifndef min +#define min(x,y) (((x)<(y))?(x):(y)) +#endif +#endif +#endif /* __cplusplus */ + +#ifdef __cplusplus +extern "C" { +#endif + +int GetLastError(); +void _splitpath(const char*, char*, char*, char*, char*); +void _makepath(char*, const char*, const char*, const char*, const char*); +char *_fullpath(char *absPath, const char *relPath, size_t maxLength); +BOOL MessageBeep(uint); +unsigned long _filelength(int fd); + +void PROFILE_Close(LPCSTR filename); + +int GetPrivateProfileString( + LPCTSTR lpAppName, // section name + LPCTSTR lpKeyName, // key name + LPCTSTR lpDefault, // default string + LPTSTR lpReturnedString, // destination buffer + int nSize, // size of destination buffer + LPCTSTR lpFileName // initialization file name + ); + +uint GetPrivateProfileInt( + LPCTSTR lpAppName, // section name + LPCTSTR lpKeyName, // key name + INT nDefault, // return value if key name not found + LPCTSTR lpFileName // initialization file name + ); + +BOOL WritePrivateProfileString( + LPCTSTR lpAppName, // section name + LPCTSTR lpKeyName, // key name + LPCTSTR lpString, // string to add + LPCTSTR lpFileName // initialization file + ); + +int GetPrivateProfileSection( + LPCTSTR lpAppName, // section name + LPTSTR lpReturnedString, // return buffer + int nSize, // size of return buffer + LPCTSTR lpFileName // initialization file name + ); + +BOOL WritePrivateProfileSection( + LPCTSTR lpAppName, // section name + LPCTSTR lpString, // data + LPCTSTR lpFileName // file name + ); + +PSZ strupr(PSZ s); +PSZ strlwr(PSZ s); + +typedef size_t FILEPOS; +//pedef int FILEHANDLE; // UNIX + +#ifdef __cplusplus +} +#endif + +#else /* WINDOWS */ +#include <windows.h> + +typedef __int64 FILEPOS; +//pedef HANDLE FILEHANDLE; // Win32 + +#endif /* WINDOWS */ + +#define XSTR(x) ((x)?(x):"<null>") + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __OSUTIL_H__ */ diff --git a/storage/connect/plgcnx.h b/storage/connect/plgcnx.h new file mode 100644 index 00000000000..33b0ba64edc --- /dev/null +++ b/storage/connect/plgcnx.h @@ -0,0 +1,218 @@ +/**************************************************************************/ +/* PLGCNX.H */ +/* Copyright to the author: Olivier Bertrand 2000-2012 */ +/* */ +/* This is the connection DLL's declares. */ +/**************************************************************************/ +#if !defined(_PLGCNX_H) +#define _PLGCNX_H + +#define MAXMSGLEN 65512 /* Default max length of cnx message */ +#define MAXERRMSG 512 /* Max length of error messages */ +#define MAXMESSAGE 256 /* Max length of returned messages */ +#define MAXDBNAME 128 /* Max length of DB related names */ + +/**************************************************************************/ +/* API Function return codes. */ +/**************************************************************************/ +enum FNRC {RC_LICENSE = 7, /* PLGConnect prompt for license key */ + RC_PASSWD = 6, /* PLGConnect prompt for User/Pwd */ + RC_SUCWINFO = 5, /* Succes With Info return code */ + RC_SOCKET = 4, /* RC from PLGConnect to socket DLL */ + RC_PROMPT = 3, /* Intermediate prompt return */ + RC_CANCEL = 2, /* Command was cancelled by user */ + RC_PROGRESS = 1, /* Intermediate progress info */ + RC_SUCCESS = 0, /* Successful function (must be 0) */ + RC_MEMORY = -1, /* Storage allocation error */ + RC_TRUNCATED = -2, /* Result has been truncated */ + RC_TIMEOUT = -3, /* Connection timeout occured */ + RC_TOOBIG = -4, /* Data is too big for connection */ + RC_KEY = -5, /* Null ptr to key in Connect */ + /* or bad key in other functions */ + RC_MAXCONN = -6, /* Too many conn's for one process */ + RC_MAXCLIENT = -7, /* Too many clients for one system */ + RC_SYNCHRO = -8, /* Synchronization error */ + RC_SERVER = -9, /* Error related to the server */ + RC_MAXCOL = -10, /* Result has too many columns */ + RC_LAST = -10}; /* Other error codes are < this and */ + /* are system errors. */ + +/**************************************************************************/ +/* Standard function return codes. */ +/**************************************************************************/ +#if !defined(RC_OK_DEFINED) +#define RC_OK_DEFINED +enum RCODE {RC_OK = 0, /* No error return code */ + RC_NF = 1, /* Not found return code */ + RC_EF = 2, /* End of file return code */ + RC_FX = 3, /* Error return code */ + RC_INFO = 4}; /* Success with info */ +#endif // !RC_OK_DEFINED + +/**************************************************************************/ +/* Data types. */ +/**************************************************************************/ +enum XDBTYPE {DB_ERROR = 0, /* Unknown or wrong type */ + DB_STRING = 1, /* Null terminated string */ + DB_CHAR = 2, /* Character array */ + DB_SHORT = 3, /* Used by some catalog functions */ + DB_INT = 4, /* Long integer array */ + DB_DOUBLE = 5, /* Double float array */ + DB_DATE = 6}; /* Datetime value array */ + +/**************************************************************************/ +/* Index of info values within the info int integer array. */ +/**************************************************************************/ +enum INFO {INDX_RC, /* Index of PlugDB return code field */ + INDX_TIME, /* Index of elapsed time in millisec */ + INDX_CHG, /* Index of Language or DB changed */ + INDX_RSAV, /* Index of Result Set availability */ + INDX_TYPE, /* Index of returned data type field */ + INDX_LINE, /* Index of number of lines field */ + INDX_LEN, /* Index of line length field */ + INDX_SIZE, /* Index of returned data size field */ + INDX_MAX}; /* Size of info array */ + +/**************************************************************************/ +/* Internal message types. */ +/**************************************************************************/ +enum MSGTYP {MST_OPEN = 10, /* Code for old connecting message */ + MST_COMMAND = 11, /* Code for send command message */ + MST_RESULT = 12, /* Code for get result message */ + MST_CLOSE = 13, /* Code for disconnecting message */ + MST_PROGRESS = 14, /* Code for progress message */ + MST_CANCEL = 15, /* Code for cancel message */ + MST_PROCESSED = 16, /* Code for already processed msg */ + MST_ERROR = 17, /* Code for get error message */ + MST_CHAR = 18, /* Code for get char value message */ + MST_LONG = 19, /* Code for get int value message */ + MST_COLUMN = 20, /* Code for get col value message */ + MST_MESSAGE = 21, /* Code for get message message */ + MST_HEADER = 22, /* Code for get header message */ + MST_SOCKET = 23, /* Code for socket error message */ + MST_SHUTDOWN = 24, /* Code for shutdown message */ + MST_SOCKPROG = 25, /* Code for socket progress message */ + MST_POST = 26, /* Code for post command message */ + MST_NEW_OPEN = 27, /* Code for new connecting message */ + MST_PROG_NUM = 5}; /* Num of integers in progress msg */ + +/**************************************************************************/ +/* Vendors. */ +/**************************************************************************/ +enum VENDOR {VDR_UNKNOWN = -2, /* Not known or not connected */ + VDR_PlugDB = -1, /* PlugDB */ + VDR_OTHER = 0}; /* OEM */ + +/**************************************************************************/ +/* Attribute keys of Result Description structure (arranged by type). */ +/**************************************************************************/ +enum CKEYS {K_ProgMsg, K_Lang, K_ActiveDB, K_Cmax}; +enum LKEYS {K_NBcol, K_NBlin, K_CurPos, K_RC, K_Result, K_Elapsed, + K_Continued, K_Maxsize, K_Lmax, K_Maxcol, + K_Maxres, K_Maxlin, K_NBparm}; +enum NKEYS {K_Type, K_Length, K_Prec, K_DataLen, K_Nmax}; + +/**************************************************************************/ +/* Result description structures. */ +/**************************************************************************/ +typedef struct _MsgTagAttr { + bool fSupplied; + char Attr[MAXMESSAGE]; + } MTAG, *PMTAG; + +typedef struct _CharTagAttr { + bool fSupplied; + char Attr[MAXDBNAME]; + } CTAG, *PCTAG; + +typedef struct _LongTagAttr { + bool fSupplied; + int Attr; + } LTAG, *PLTAG; + +typedef struct _ColVar { + LTAG Lat[K_Nmax]; + CTAG Cat; + } COLVAR, *LPCOLVAR; + +typedef struct _ResDesc { + int Maxcol; /* Max number of columns */ + int Colnum; /* Number of columns */ + MTAG Mat; /* Message */ + CTAG Cat[K_Cmax]; /* Character attributes */ + LTAG Lat[K_Lmax]; /* Long int attributes */ + COLVAR Col[1]; /* Column attributes */ + } RDESC, *PRDESC; + +/**************************************************************************/ +/* Exported PlugDB client functions in Plgcnx DLL. */ +/**************************************************************************/ +#if !defined(CNXFUNC) +#if defined(UNIX) || defined(UNIV_LINUX) +#undef __stdcall +#define __stdcall +#endif + +#if defined(NOLIB) /* Dynamic link of plgcnx.dll */ +#define CNXFUNC(f) (__stdcall *f) +#else /* LIB */ /* Static link with plgcnx.lib */ +#define CNXFUNC(f) __stdcall f +#endif +#endif + +#if !defined(CNXKEY) +#define CNXKEY uint +#endif + +#if !defined(XTRN) +#define XTRN +#endif + +#ifdef NOT_USED +//#if !defined(NO_FUNC) +#ifdef __cplusplus +extern "C" { +#endif + +XTRN int CNXFUNC(PLGConnect) (CNXKEY *, const char *, bool); +XTRN int CNXFUNC(PLGSendCommand) (CNXKEY, const char *, void *, int, int *); +XTRN int CNXFUNC(PLGGetResult) (CNXKEY, void *, int, int *, bool); +XTRN int CNXFUNC(PLGDisconnect) (CNXKEY); +XTRN int CNXFUNC(PLGGetErrorMsg) (CNXKEY, char *, int, int *); +XTRN bool CNXFUNC(PLGGetCharValue)(CNXKEY, char *, int, int); +XTRN bool CNXFUNC(PLGGetIntValue)(CNXKEY, int *, int); +XTRN bool CNXFUNC(PLGGetColValue) (CNXKEY, int *, int, int); +XTRN bool CNXFUNC(PLGGetMessage) (CNXKEY, char *, int); +XTRN bool CNXFUNC(PLGGetHeader) (CNXKEY, char *, int, int); + +#ifdef __cplusplus +} +#endif +//#endif /* !NO_FUNC */ + +/**************************************************************************/ +/* Convenience function Definitions */ +/**************************************************************************/ +#define PLGPostCommand(T,C) PLGSendCommand(T,C,NULL,0,NULL) +#if defined(FNCMAC) +#define PLGGetProgMsg(T,C,S) PLGGetCharValue(T,C,S,K_ProgMsg) +#define PLGGetLangID(T,C,S) PLGGetCharValue(T,C,S,K_Lang) +#define PLGGetActiveDB(T,C,S) PLGGetCharValue(T,C,S,K_ActiveDB) +#define PLGGetCursorPos(T,L) PLGGetIntValue(T,L,K_CurPos) +#define PLGGetResultType(T,L) PLGGetIntValue(T,L,K_Result) +#define PLGGetNBcol(T,L) PLGGetIntValue(T,L,K_NBcol) +#define PLGGetNBlin(T,L) PLGGetIntValue(T,L,K_NBlin) +#define PLGGetRetCode(T,L) PLGGetIntValue(T,L,K_RC) +#define PLGGetElapsed(T,L) PLGGetIntValue(T,L,K_Elapsed) +#define PLGGetContinued(T,L) PLGGetIntValue(T,L,K_Continued) +#define PLGGetMaxSize(T,L) PLGGetIntValue(T,L,K_Maxsize) +#define PLGGetLength(T,L,C) PLGGetColValue(T,L,K_Length,C) +#define PLGGetDataSize(T,L,C) PLGGetColValue(T,L,K_DataLen,C) +#define PLGGetDecimal(T,L,C) PLGGetColValue(T,L,K_Prec,C) +#define PLGGetType(T,L,C) PLGGetColValue(T,L,K_Type,C) +#endif /* FNCMAC */ +#endif // NOT_USED + +#endif /* !_PLGCNX_H */ + +/* ------------------------- End of Plgcnx.h ---------------------------- */ diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h new file mode 100644 index 00000000000..611b0067e18 --- /dev/null +++ b/storage/connect/plgdbsem.h @@ -0,0 +1,580 @@ +/************** PlgDBSem H Declares Source Code File (.H) **************/ +/* Name: PLGDBSEM.H Version 3.5 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* This file contains the PlugDB++ application type definitions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include required application header files */ +/***********************************************************************/ +#include "checklvl.h" + +/***********************************************************************/ +/* DB Constant definitions. */ +/***********************************************************************/ +#if defined(FRENCH) +#define DEFAULT_LOCALE "French" +#else // !FRENCH +#define DEFAULT_LOCALE "English" +#endif // !FRENCH + +#define DOS_MAX_PATH 144 /* Must be the same across systems */ +#define DOS_BUFF_LEN 100 /* Number of lines in binary file buffer */ +#undef DOMAIN /* For Unix version */ + +enum BLKTYP {TYPE_TABLE = 50, /* Table Name/Correl Block */ + TYPE_COLUMN = 51, /* Column Name/Correl Block */ +// TYPE_OPVAL = 52, /* Operator value (OPVAL) */ + TYPE_TDB = 53, /* Table Description Block */ + TYPE_COLBLK = 54, /* Column Description Block */ + TYPE_PSZ = 64, /* Pointer to String ended by 0 */ + TYPE_SQL = 65, /* Pointer to SQL block */ + TYPE_XOBJECT = 69, /* Extended DB object */ + TYPE_COLCRT = 71, /* Column creation block */ + TYPE_CONST = 72, /* Constant */ +// TYPE_INDEXDEF = 73, /* Index definition block */ +// TYPE_OPER = 74, /* Operator block (OPER) */ + +/*-------------------- type tokenized string --------------------------*/ + TYPE_DATE = 8, /* Timestamp */ +/*-------------------- additional values used by LNA ------------------*/ + TYPE_COLIST = 14, /* Column list */ + TYPE_COL = 41, /* Column */ +/*-------------------- types used by scalar functions -----------------*/ + TYPE_NUM = 12, + TYPE_UNDEF = 13, +/*-------------------- file blocks used when closing ------------------*/ + TYPE_FB_FILE = 22, /* File block (stream) */ + TYPE_FB_MAP = 23, /* Mapped file block (storage) */ + TYPE_FB_HANDLE = 24, /* File block (handle) */ + TYPE_FB_XML = 21, /* DOM XML file block */ + TYPE_FB_XML2 = 27}; /* libxml2 XML file block */ + +enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */ + TAB_DOS = 1, /* Fixed column offset, variable LRECL */ + TAB_FIX = 2, /* Fixed column offset, fixed LRECL */ + TAB_BIN = 3, /* Like FIX but can have binary fields */ + TAB_CSV = 4, /* DOS files with CSV records */ + TAB_FMT = 5, /* DOS files with formatted recordss */ + TAB_DBF = 6, /* DBF Dbase or Foxpro files */ + TAB_XML = 7, /* XML or HTML files */ + TAB_INI = 8, /* INI or CFG files */ + TAB_VEC = 9, /* Vector column arrangement */ + TAB_ODBC = 10, /* Table accessed via (unix)ODBC */ + TAB_MYSQL = 11, /* MySQL table accessed via MySQL API */ + TAB_DIR = 12, /* Returns a list of files */ + TAB_MAC = 13, /* MAC address (Windows only) */ + TAB_WMI = 14, /* WMI tables (Windows only) */ + TAB_TBL = 15, /* Collection of CONNECT tables */ + TAB_OEM = 16, /* OEM implemented table */ + TAB_CATLG = 17, /* Catalog table */ + TAB_PLG = 18, /* PLG NIY */ + TAB_PIVOT = 19, /* PIVOT NIY */ + TAB_JCT = 20, /* Junction tables NIY */ + TAB_DMY = 21, /* DMY Dummy tables NIY */ + TAB_NIY = 22}; /* Table not implemented yet */ + +enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */ + TYPE_AM_ROWID = 1, /* ROWID type (special column) */ + TYPE_AM_FILID = 2, /* FILEID type (special column) */ + TYPE_AM_TAB = 3, /* Table (any type) */ + TYPE_AM_VIEW = 4, /* VIEW (any type) */ + TYPE_AM_SRVID = 5, /* SERVID type (special column) */ + TYPE_AM_TABID = 6, /* TABID type (special column) */ + TYPE_AM_CNSID = 7, /* CONSTID type (special column) */ + TYPE_AM_COUNT = 10, /* CPT AM type no (count table) */ + TYPE_AM_DCD = 20, /* Decode access method type no */ + TYPE_AM_CMS = 30, /* CMS access method type no */ + TYPE_AM_MAP = 32, /* MAP access method type no */ + TYPE_AM_FMT = 33, /* DOS files with formatted recs */ + TYPE_AM_CSV = 34, /* DOS files with CSV records */ + TYPE_AM_MCV = 35, /* MAP files with CSV records */ + TYPE_AM_DOS = 36, /* DOS am with Lrecl = V */ + TYPE_AM_FIX = 38, /* DOS am with Lrecl = F */ + TYPE_AM_BIN = 39, /* DOS am with Lrecl = B */ + TYPE_AM_VCT = 40, /* VCT access method type no */ + TYPE_AM_VMP = 43, /* VMP access method type no */ + TYPE_AM_QRY = 50, /* QRY access method type no */ + TYPE_AM_QRS = 51, /* QRYRES access method type no */ + TYPE_AM_SQL = 60, /* SQL VIEW access method type */ + TYPE_AM_PLG = 70, /* PLG access method type no */ + TYPE_AM_PLM = 71, /* PDM access method type no */ + TYPE_AM_DOM = 80, /* DOM access method type no */ + TYPE_AM_DIR = 90, /* DIR access method type no */ + TYPE_AM_ODBC = 100, /* ODBC access method type no */ + TYPE_AM_OEM = 110, /* OEM access method type no */ + TYPE_AM_TBL = 115, /* TBL access method type no */ + TYPE_AM_PIVOT = 120, /* PIVOT access method type no */ + TYPE_AM_SRC = 121, /* PIVOT multiple column type no */ + TYPE_AM_FNC = 122, /* PIVOT source column type no */ + TYPE_AM_XTB = 130, /* SYS table access method type */ + TYPE_AM_MAC = 137, /* MAC table access method type */ + TYPE_AM_WMI = 139, /* WMI table access method type */ + TYPE_AM_XCL = 140, /* SYS column access method type */ + TYPE_AM_INI = 150, /* INI files access method */ + TYPE_AM_TFC = 155, /* TFC (Circa) (Fuzzy compare) */ + TYPE_AM_DBF = 160, /* DBF Dbase files am type no */ + TYPE_AM_JCT = 170, /* Junction tables am type no */ + TYPE_AM_DMY = 172, /* DMY Dummy tables am type no */ + TYPE_AM_SET = 180, /* SET Set tables am type no */ + TYPE_AM_MYSQL = 192, /* MYSQL access method type no */ + TYPE_AM_CAT = 193, /* Catalog access method type no */ + TYPE_AM_OUT = 200}; /* Output relations (storage) */ + +enum RECFM {RECFM_NAF = -2, /* Not a file */ + RECFM_OEM = -1, /* OEM file access method */ + RECFM_VAR = 0, /* Varying length DOS files */ + RECFM_FIX = 1, /* Fixed length DOS files */ + RECFM_BIN = 2, /* Binary DOS files (also fixed) */ + RECFM_VCT = 3, /* VCT formatted files */ + RECFM_ODBC = 4, /* Table accessed via ODBC */ + RECFM_PLG = 5, /* Table accessed via PLGconn */ + RECFM_DBF = 6}; /* DBase formatted file */ + +#if 0 +enum MISC {DB_TABNO = 1, /* DB routines in Utility Table */ + MAX_MULT_KEY = 10, /* Max multiple key number */ + NAM_LEN = 128, /* Length of col and tab names */ + ARRAY_SIZE = 50, /* Default array block size */ + MAXRES = 500, /* Default maximum result lines */ + MAXLIN = 10000, /* Default maximum data lines */ + MAXBMP = 32}; /* Default XDB2 max bitmap size */ + +enum ALGMOD {AMOD_AUTO = 0, /* PLG chooses best algorithm */ + AMOD_SQL = 1, /* Use SQL algorithm */ + AMOD_QRY = 2}; /* Use QUERY algorithm */ +#else // !0 +#define NAM_LEN 128 +#endif // !0 + +enum MODE {MODE_ANY = 0, /* Unspecified mode */ + MODE_READ = 10, /* Input/Output mode */ + MODE_WRITE = 20, /* Input/Output mode */ + MODE_UPDATE = 30, /* Input/Output mode */ + MODE_INSERT = 40, /* Input/Output mode */ + MODE_DELETE = 50}; /* Input/Output mode */ + +#if !defined(RC_OK_DEFINED) +#define RC_OK_DEFINED +enum RCODE {RC_OK = 0, /* No error return code */ + RC_NF = 1, /* Not found return code */ + RC_EF = 2, /* End of file return code */ + RC_FX = 3, /* Error return code */ + RC_INFO = 4}; /* Success with info */ +#endif // !RC_OK_DEFINED + +enum OPVAL {OP_EQ = 1, /* Filtering operator = */ + OP_NE = 2, /* Filtering operator != */ + OP_GT = 3, /* Filtering operator > */ + OP_GE = 4, /* Filtering operator >= */ + OP_LT = 5, /* Filtering operator < */ + OP_LE = 6, /* Filtering operator <= */ + OP_IN = 7, /* Filtering operator IN */ + OP_NULL = 8, /* Filtering operator IS NULL */ + OP_EXIST = 9, /* Filtering operator EXISTS */ + OP_LIKE = 10, /* Filtering operator LIKE */ + OP_LOJ = -1, /* Filter op LEFT OUTER JOIN */ + OP_ROJ = -2, /* Filter op RIGHT OUTER JOIN */ + OP_DTJ = -3, /* Filter op DISTINCT JOIN */ + OP_XX = 11, /* Filtering operator unknown */ + OP_AND = 12, /* Filtering operator AND */ + OP_OR = 13, /* Filtering operator OR */ + OP_CNC = 14, /* Expression Concat operator */ + OP_NOT = 15, /* Filtering operator NOT */ + OP_SEP = 20, /* Filtering separator */ + OP_ADD = 16, /* Expression Add operator */ + OP_SUB = 17, /* Expression Substract operator */ + OP_MULT = 18, /* Expression Multiply operator */ + OP_DIV = 19, /* Expression Divide operator */ + OP_NOP = 21, /* Scalar function is nopped */ + OP_NUM = 22, /* Scalar function Op Num */ + OP_ABS = 23, /* Scalar function Op Abs */ + OP_MAX = 24, /* Scalar function Op Max */ + OP_MIN = 25, /* Scalar function Op Min */ + OP_CEIL = 26, /* Scalar function Op Ceil */ + OP_FLOOR = 27, /* Scalar function Op Floor */ + OP_MOD = 28, /* Scalar function Op Mod */ + OP_ROUND = 29, /* Scalar function Op Round */ + OP_SIGN = 30, /* Scalar function Op Sign */ + OP_LEN = 31, /* Scalar function Op Len */ + OP_INSTR = 32, /* Scalar function Op Instr */ + OP_LEFT = 33, /* Scalar function Op Left */ + OP_RIGHT = 34, /* Scalar function Op Right */ + OP_ASCII = 35, /* Scalar function Op Ascii */ + OP_EXP = 36, /* Scalar function Op Exp */ + OP_LN = 37, /* Scalar function Op Ln */ + OP_LOG = 38, /* Scalar function Op Log */ + OP_POWER = 39, /* Scalar function Op Power */ + OP_SQRT = 40, /* Scalar function Op Sqrt */ + OP_COS = 41, /* Scalar function Op Cos */ + OP_COSH = 42, /* Scalar function Op Cosh */ + OP_SIN = 43, /* Scalar function Op Sin */ + OP_SINH = 44, /* Scalar function Op Sinh */ + OP_TAN = 45, /* Scalar function Op Tan */ + OP_TANH = 46, /* Scalar function Op Tanh */ + OP_USER = 47, /* Scalar function Op User */ + OP_CHAR = 48, /* Scalar function Op Char */ + OP_UPPER = 49, /* Scalar function Op Upper */ + OP_LOWER = 50, /* Scalar function Op Lower */ + OP_RPAD = 51, /* Scalar function Op Rpad */ + OP_LPAD = 52, /* Scalar function Op Lpad */ + OP_LTRIM = 53, /* Scalar function Op Ltrim */ + OP_RTRIM = 54, /* Scalar function Op Rtrim */ + OP_REPL = 55, /* Scalar function Op Replace */ + OP_SUBST = 56, /* Scalar function Op Substr */ + OP_LJUST = 57, /* Scalar function Op Ljustify */ + OP_RJUST = 58, /* Scalar function Op Rjustify */ + OP_CJUST = 59, /* Scalar function Op Cjustify */ + OP_ENCODE = 60, /* Scalar function Op Encode */ + OP_DECODE = 61, /* Scalar function Op Decode */ + OP_SEQU = 62, /* Scalar function Op Sequence */ + OP_IF = 63, /* Scalar function Op If */ + OP_STRING = 64, /* Scalar function Op String */ + OP_TOKEN = 65, /* Scalar function Op Token */ + OP_SNDX = 66, /* Scalar function Op Soundex */ + OP_DATE = 67, /* Scalar function Op Date */ + OP_MDAY = 68, /* Scalar function Op Month Day */ + OP_MONTH = 69, /* Scalar function Op Month of */ + OP_YEAR = 70, /* Scalar function Op Year of */ + OP_WDAY = 71, /* Scalar function Op Week Day */ + OP_YDAY = 72, /* Scalar function Op Year Day */ + OP_DBTWN = 73, /* Scalar function Op Days betwn */ + OP_MBTWN = 74, /* Scalar function Op Months btw */ + OP_YBTWN = 75, /* Scalar function Op Years btwn */ + OP_ADDAY = 76, /* Scalar function Op Add Days */ + OP_ADDMTH = 77, /* Scalar function Op Add Months */ + OP_ADDYR = 78, /* Scalar function Op Add Years */ + OP_NXTDAY = 79, /* Scalar function Op Next Day */ + OP_SYSDT = 80, /* Scalar function Op SysDate */ + OP_DELTA = 81, /* Scalar function Op Delta */ + OP_LAST = 82, /* Scalar function Op Last */ + OP_IFF = 83, /* Scalar function Op Iff */ + OP_MAVG = 84, /* Scalar function Op Moving Avg */ + OP_VWAP = 85, /* Scalar function Op VWAP */ + OP_TIME = 86, /* Scalar function Op TIME */ + OP_SETLEN = 87, /* Scalar function Op Set Length */ + OP_TRANSL = 88, /* Scalar function Op Translate */ + OP_BITAND = 89, /* Expression BitAnd operator */ + OP_BITOR = 90, /* Expression BitOr operator */ + OP_BITXOR = 91, /* Expression XOR operator */ + OP_BITNOT = 92, /* Expression Complement operator*/ + OP_CNTIN = 93, /* Scalar function Count In */ + OP_FDISK = 94, /* Scalar function Disk of fileid*/ + OP_FPATH = 95, /* Scalar function Path of fileid*/ + OP_FNAME = 96, /* Scalar function Name of fileid*/ + OP_FTYPE = 97, /* Scalar function Type of fileid*/ + OP_XDATE = 98, /* Scalar function Op Fmt Date */ + OP_SWITCH = 99, /* Scalar function Op Switch */ + OP_EXIT = 100, /* Scalar function Op Exit */ + OP_LIT = 101, /* Scalar function Op Literal */ + OP_LOCALE = 102, /* Scalar function Op Locale */ + OP_FRNCH = 103, /* Scalar function Op French */ + OP_ENGLSH = 104, /* Scalar function Op English */ + OP_RAND = 105, /* Scalar function Op Rand(om) */ + OP_FIRST = 106, /* Index operator Find First */ + OP_NEXT = 107, /* Index operator Find Next */ + OP_SAME = 108, /* Index operator Find Next Same */ + OP_FSTDIF = 109, /* Index operator Find First dif */ + OP_NXTDIF = 110, /* Index operator Find Next dif */ + OP_VAL = 111, /* Scalar function Op Valist */ + OP_QUART = 112, /* Scalar function Op QUARTER */ + OP_CURDT = 113, /* Scalar function Op CurDate */ + OP_NWEEK = 114, /* Scalar function Op Week number*/ + OP_ROW = 115, /* Scalar function Op Row */ + OP_SYSTEM = 200, /* Scalar function Op System */ + OP_REMOVE = 201, /* Scalar function Op Remove */ + OP_RENAME = 202, /* Scalar function Op Rename */ + OP_FCOMP = 203}; /* Scalar function Op Compare */ + +enum TUSE {USE_NO = 0, /* Table is not yet linearized */ + USE_LIN = 1, /* Table is linearized */ + USE_READY = 2, /* Column buffers are allocated */ + USE_OPEN = 3, /* Table is open */ + USE_CNT = 4, /* Specific to LNA */ + USE_NOKEY = 5}; /* Specific to SqlToHql */ + +/***********************************************************************/ +/* Following definitions are used to indicate the status of a column. */ +/***********************************************************************/ +enum STATUS {BUF_NO = 0x00, /* Column buffer not allocated */ + BUF_EMPTY = 0x01, /* Column buffer is empty */ + BUF_READY = 0x02, /* Column buffer is ready */ + BUF_READ = 0x04, /* Column buffer has read value */ + BUF_MAPPED = 0x08}; /* Used by the VMPFAM class */ + +/***********************************************************************/ +/* Following definitions are used to indicate how a column is used. */ +/* Corresponding bits are ON if the column is used in: */ +/***********************************************************************/ +enum COLUSE {U_P = 0x01, /* the projection list. */ + U_J_EXT = 0x02, /* a join filter. */ + U_J_INT = 0x04, /* a join after linearisation. */ +/*-- Such a column have a constant value throughout a subquery eval. --*/ + U_CORREL = 0x08, /* a correlated sub-query */ +/*-------------------- additional values used by CONNECT --------------*/ + U_VAR = 0x10, /* a VARCHAR column */ + U_VIRTUAL = 0x20, /* a VIRTUAL column */ + U_NULLS = 0x40, /* The column may have nulls */ + U_IS_NULL = 0x80}; /* The column has a null value */ + +/***********************************************************************/ +/* DB description class and block pointer definitions. */ +/***********************************************************************/ +typedef class XTAB *PTABLE; +typedef class COLUMN *PCOLUMN; +typedef class XOBJECT *PXOB; +typedef class COLBLK *PCOL; +typedef class TBX *PTBX; +typedef class TDB *PTDB; +typedef void *PSQL; // Not used +typedef class TDBASE *PTDBASE; +typedef class TDBDOS *PTDBDOS; +typedef class TDBFIX *PTDBFIX; +typedef class TDBFMT *PTDBFMT; +typedef class TDBCSV *PTDBCSV; +typedef class TDBDOM *PTDBDOM; +typedef class TDBDIR *PTDBDIR; +typedef class DOSCOL *PDOSCOL; +typedef class CSVCOL *PCSVCOL; +typedef class MAPCOL *PMAPCOL; +typedef class TDBMFT *PTDBMFT; +typedef class TDBMCV *PTDBMCV; +typedef class MCVCOL *PMCVCOL; +typedef class RESCOL *PRESCOL; +typedef class XXBASE *PKXBASE; +typedef class KXYCOL *PXCOL; +typedef class CATALOG *PCATLG; +typedef class RELDEF *PRELDEF; +typedef class TABDEF *PTABDEF; +typedef class DOSDEF *PDOSDEF; +typedef class CSVDEF *PCSVDEF; +typedef class VCTDEF *PVCTDEF; +typedef class PIVOTDEF *PPIVOTDEF; +typedef class DOMDEF *PDOMDEF; +typedef class DIRDEF *PDIRDEF; +typedef class OEMDEF *POEMDEF; +typedef class COLCRT *PCOLCRT; +typedef class COLDEF *PCOLDEF; +typedef class CONSTANT *PCONST; +typedef class VALUE *PVAL; +typedef class VALBLK *PVBLK; + +typedef struct _fblock *PFBLOCK; +typedef struct _mblock *PMBLOCK; +typedef struct _cblock *PCBLOCK; +typedef struct _tabs *PTABS; +typedef struct _qryres *PQRYRES; +typedef struct _colres *PCOLRES; +typedef struct _datpar *PDTP; +typedef struct indx_used *PXUSED; + +/***********************************************************************/ +/* Utility blocks for file and storage. */ +/***********************************************************************/ +typedef struct _fblock { /* Opened (mapped) file block */ + struct _fblock *Next; + LPCSTR Fname; /* Point on file name */ + size_t Length; /* File length (<4GB) */ + short Count; /* Nb of times map is used */ + short Type; /* TYPE_FB_FILE or TYPE_FB_MAP */ + MODE Mode; /* Open mode */ + char *Memory; /* Pointer to file mapping view */ + void *File; /* FILE pointer */ + HANDLE Handle; /* File handle */ + } FBLOCK; + +typedef struct _mblock { /* Memory block */ + PMBLOCK Next; + bool Inlist; /* True if in mblock list */ + size_t Size; /* Size of allocation */ + bool Sub; /* True if suballocated */ + void *Memp; /* Memory pointer */ + } MBLOCK; + +/***********************************************************************/ +/* The QUERY application User Block. */ +/***********************************************************************/ +typedef struct { /* User application block */ +//void *Act2; /* RePoint to activity block */ +//short LineLen; /* Current output line len */ + NAME Name; /* User application name */ +//NAME Password; /* User application password */ +//PSZ UserFile; /* User application filename */ + char Server[17]; /* Server name */ + char DBName[17]; /* Current database name */ +//char Host[65]; /* Caller's host name */ +//char User[17]; /* Caller's user name */ +//uint Granted; /* Grant bitmap */ + PCATLG Catalog; /* To CATALOG class */ + PQRYRES Result; /* To query result blocks */ + PFBLOCK Openlist; /* To file/map open list */ + PMBLOCK Memlist; /* To memory block list */ + PXUSED Xlist; /* To used index list */ +//int Maxres; /* Result Max nb of lines */ +//int Maxtmp; /* Intermediate tables Maxres */ +//int Maxlin; /* Query Max nb of data lines */ +//int Maxbmp; /* Maximum XDB2 bitmap size */ + int Check; /* General level of checking */ + int Numlines; /* Number of lines involved */ +//ALGMOD AlgChoice; /* Choice of algorithm mode */ +//AREADEF DescArea; /* Table desc. area size */ + USETEMP UseTemp; /* Use temporary file */ +//int Curtype; /* 0: static else: dynamic */ + int Vtdbno; /* Used for TDB number setting */ + bool Remote; /* true: if remotely called */ +//bool NotFinal; /* true: for intermediate table */ + bool Proginfo; /* true: return progress info */ + bool Subcor; /* Used for Progress info */ + size_t ProgMax; /* Used for Progress info */ + size_t ProgCur; /* Used for Progress info */ + size_t ProgSav; /* Used for Progress info */ + LPCSTR Step; /* Execution step name */ +//char Work[_MAX_PATH]; /* Local work path */ + } DBUSERBLK, *PDBUSER; + +/***********************************************************************/ +/* Column output format. */ +/***********************************************************************/ +typedef struct _format { /* Format descriptor block */ + char Type[2]; /* C:char, F:double, N:int, Dx: date */ + ushort Length; /* Output length */ + short Prec; /* Output precision */ + } FORMAT, *PFORMAT; + +/***********************************************************************/ +/* Definition of blocks used in type and copy routines. */ +/***********************************************************************/ +typedef struct _tabptr { /* start=P1 */ + struct _tabptr *Next; + int Num; /* alignement */ + void *Old[50]; + void *New[50]; /* old and new values of copied ptrs */ + } TABPTR, *PTABPTR; + +typedef struct _tabadr { /* start=P3 */ + struct _tabadr *Next; + int Num; + void *Adx[50]; /* addr of pointers to be reset */ + } TABADR, *PTABADR; + +typedef struct _tabs { + PGLOBAL G; + PTABPTR P1; + PTABADR P3; + } TABS; + +/***********************************************************************/ +/* Following definitions are used to define table fields (columns). */ +/***********************************************************************/ +enum XFLD {FLD_NO = 0, /* Not a field definition item */ + FLD_NAME = 1, /* Item name */ + FLD_TYPE = 2, /* Field type */ + FLD_TYPENAME = 3, /* Field type name */ + FLD_PREC = 4, /* Field precision (length?) */ + FLD_LENGTH = 5, /* Field length (?) */ + FLD_SCALE = 6, /* Field scale (precision) */ + FLD_RADIX = 7, /* Field radix */ + FLD_NULL = 8, /* Field nullable property */ + FLD_REM = 9, /* Field comment (remark) */ + FLD_CHARSET = 10, /* Field collation */ + FLD_KEY = 11, /* Field key property */ + FLD_DEFAULT = 12, /* Field default value */ + FLD_PRIV = 13, /* Field priviledges */ + FLD_DATEFMT = 14, /* Field date format */ + FLD_QUALIF = 15, /* Table qualifier */ + FLD_OWNER = 16, /* Table owner */ + FLD_TABNAME = 17}; /* Column Table name */ + +/***********************************************************************/ +/* Result of last SQL noconv query. */ +/***********************************************************************/ +typedef struct _qryres { + PCOLRES Colresp; /* Points to columns of result */ + bool Continued; /* true when more rows to fetch */ + bool Truncated; /* true when truncated by maxres */ + bool Suball; /* true when entirely suballocated */ + bool Info; /* true when info msg generated */ + int Maxsize; /* Max query number of lines */ + int Maxres; /* Allocation size */ + int Nblin; /* Number of rows in result set */ + int Nbcol; /* Number of columns in result set */ + int Cursor; /* Starting position to get data */ + int BadLines; /* Skipped bad lines in table file */ + } QRYRES, *PQRYRES; + +typedef struct _colres { + PCOLRES Next; /* To next result column */ + PCOL Colp; /* To matching column block */ + PSZ Name; /* Column header */ + PVBLK Kdata; /* Column block of values */ + char *Nulls; /* Column null value array */ + int Type; /* Internal type */ + int DBtype; /* Data type */ + int Datasize; /* Overall data size */ + int Ncol; /* Column number */ + int Clen; /* Data individual internal size */ + int Length; /* Data individual print length */ + int Prec; /* Precision */ + XFLD Fld; /* Type of field info */ + } COLRES; + +#if defined(WIN32) && !defined(NOEX) +#define DllExport __declspec( dllexport ) +#else // !WIN32 +#define DllExport +#endif // !WIN32 + +/***********************************************************************/ +/* Utility routines. */ +/***********************************************************************/ +PPARM Vcolist(PGLOBAL, PTDB, PSZ, bool); +void PlugPutOut(PGLOBAL, FILE *, short, void *, uint); +void PlugLineDB(PGLOBAL, PSZ, short, void *, uint); +char *PlgGetDataPath(PGLOBAL g); +void *PlgDBalloc(PGLOBAL, void *, MBLOCK&); +void *PlgDBrealloc(PGLOBAL, void *, MBLOCK&, size_t); +void AddPointer(PTABS, void *); +PDTP MakeDateFormat(PGLOBAL, PSZ, bool, bool, int); +int ExtractDate(char *, PDTP, int, int val[6]); + +/**************************************************************************/ +/* Allocate the result structure that will contain result data. */ +/**************************************************************************/ +PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, + int *dbtype, int *buftyp, XFLD *fldtyp, + unsigned int *length, bool blank, bool nonull); + +/***********************************************************************/ +/* Exported utility routines. */ +/***********************************************************************/ +DllExport FILE *PlugOpenFile(PGLOBAL, LPCSTR, LPCSTR); +DllExport int PlugCloseFile(PGLOBAL, PFBLOCK, bool all = false); +DllExport void PlugCleanup(PGLOBAL, bool); +DllExport bool GetPromptAnswer(PGLOBAL, char *); +DllExport char *GetAmName(PGLOBAL g, AMT am, void *memp = NULL); +DllExport PDBUSER PlgMakeUser(PGLOBAL g); +DllExport PDBUSER PlgGetUser(PGLOBAL g); +DllExport PCATLG PlgGetCatalog(PGLOBAL g, bool jump = true); +DllExport bool PlgSetXdbPath(PGLOBAL g, PSZ, PSZ, char *, int, char *, int); +DllExport void PlgDBfree(MBLOCK&); +//lExport PSZ GetIniString(PGLOBAL, void *, LPCSTR, LPCSTR, LPCSTR, LPCSTR); +//lExport int GetIniSize(char *, char *, char *, char *); +//lExport bool WritePrivateProfileInt(LPCSTR, LPCSTR, int, LPCSTR); +DllExport void NewPointer(PTABS, void *, void *); + + +#define MSGID_NONE 0 +#define MSGID_CANNOT_OPEN 1 +#define MSGID_OPEN_MODE_ERROR 2 +#define MSGID_OPEN_STRERROR 3 +#define MSGID_OPEN_ERROR_AND_STRERROR 4 +#define MSGID_OPEN_MODE_STRERROR 5 +#define MSGID_OPEN_EMPTY_FILE 6 + +FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode); +int global_open(GLOBAL *g, int msgid, const char *filename, int flags); +int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode); + +bool PushWarning(PGLOBAL, PTDBASE); diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp new file mode 100644 index 00000000000..6d1e502945b --- /dev/null +++ b/storage/connect/plgdbutl.cpp @@ -0,0 +1,1653 @@ +/********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: PLGDBUTL */ +/* ------------- */ +/* Version 3.8 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* Utility functions used by DB semantic routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* See Readme.C for a list and description of required SYSTEM files. */ +/* */ +/* PLGDBUTL.C - Source code */ +/* GLOBAL.H - Global declaration file */ +/* PLGDBSEM.H - DB application declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* OS2.LIB - OS2 libray */ +/* LLIBCE.LIB - Protect mode/standard combined large model C */ +/* library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, MS, Borland or GNU C++ Compiler */ +/* IBM, MS, Borland or GNU Linker */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#define BIGMEM 1048576 // 1 Megabyte +#else // !WIN32 +#include <unistd.h> +#include <fcntl.h> +#if defined(THREAD) +#include <pthread.h> +#endif // THREAD +#include <stdarg.h> +#define BIGMEM 2147483647 // Max int value +#endif // !WIN32 +#include <locale.h> + +/***********************************************************************/ +/* Include application header files */ +/***********************************************************************/ +#include "global.h" // header containing all global declarations. +#include "plgdbsem.h" // header containing the DB applic. declarations. +#include "preparse.h" // For DATPAR +#include "osutil.h" +#include "maputil.h" +#include "catalog.h" +#include "colblk.h" +#include "xtable.h" // header of TBX, TDB and TDBASE classes +#include "tabcol.h" // header of XTAB and COLUMN classes +#include "valblk.h" + +/***********************************************************************/ +/* Macro or external routine definition */ +/***********************************************************************/ +#if defined(THREAD) +#if defined(WIN32) +extern CRITICAL_SECTION parsec; // Used calling the Flex parser +#else // !WIN32 +extern pthread_mutex_t parmut; +#endif // !WIN32 +#endif // THREAD + +#define PLGINI "plugdb.ini" /* Configuration settings file */ +#define PLGXINI "plgcnx.ini" /* Configuration settings file */ + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +bool Initdone = false; +bool plugin = false; // True when called by the XDB plugin handler + +extern "C" { + char plgxini[_MAX_PATH] = PLGXINI; + char plgini[_MAX_PATH] = PLGINI; +#if defined(WIN32) + char nmfile[_MAX_PATH] = ".\\Log\\plugdb.out"; + char pdebug[_MAX_PATH] = ".\\Log\\plgthread.out"; + + HINSTANCE s_hModule; // Saved module handle +#else // !WIN32 + char nmfile[_MAX_PATH] = "./Log/plugdb.out"; + char pdebug[_MAX_PATH] = "./Log/plgthread.out"; +#endif // !WIN32 + +#if defined(XMSG) + char msglang[16] = "ENGLISH"; // Default language +#endif +} // extern "C" + +extern "C" int trace; +extern "C" char version[]; + +// The debug trace used by the main thread + FILE *pfile = NULL; + +MBLOCK Nmblk = {NULL, false, 0, false, NULL}; // Used to init MBLOCK's + +/***********************************************************************/ +/* Routines called externally and internally by utility routines. */ +/***********************************************************************/ +bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool); +bool EvalLikePattern(LPCSTR, LPCSTR); +void PlugConvertConstant(PGLOBAL, void* &, short&); + +#ifdef DOMDOC_SUPPORT +void CloseXMLFile(PGLOBAL, PFBLOCK, bool); +#endif // DOMDOC_SUPPORT + +#ifdef LIBXML2_SUPPORT +void CloseXML2File(PGLOBAL, PFBLOCK, bool); +#endif // LIBXML2_SUPPORT + +extern "C" int GetRcString(int id, char *buf, int bufsize); + +/***********************************************************************/ +/* Routines for file IO with error reporting to g->Message */ +/***********************************************************************/ +static void +global_open_error_msg(GLOBAL *g, int msgid, const char *path, const char *mode) +{ + int len; + switch (msgid) + { + case MSGID_CANNOT_OPEN: + len= snprintf(g->Message, sizeof(g->Message) - 1, + MSG(CANNOT_OPEN), // Cannot open %s + path); + break; + + case MSGID_OPEN_MODE_ERROR: + len= snprintf(g->Message, sizeof(g->Message) - 1, + MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s" + mode, (int) errno, path); + break; + + case MSGID_OPEN_MODE_STRERROR: + len= snprintf(g->Message, sizeof(g->Message) - 1, + MSG(OPEN_MODE_ERROR) ": %s", // Open(%s) error %d on %s: %s + mode, (int) errno, path, strerror(errno)); + break; + + case MSGID_OPEN_STRERROR: + len= snprintf(g->Message, sizeof(g->Message) - 1, + MSG(OPEN_STRERROR), // "open error: %s" + strerror(errno)); + break; + + case MSGID_OPEN_ERROR_AND_STRERROR: + len= snprintf(g->Message, sizeof(g->Message) - 1, + //OPEN_ERROR does not work, as it wants mode %d (not %s) + //MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s" + "Open error %d in mode %s on %s: %s", + errno, mode, path, strerror(errno)); + break; + + case MSGID_OPEN_EMPTY_FILE: + len= snprintf(g->Message, sizeof(g->Message) - 1, + MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s" + path, strerror(errno)); + break; + + default: + DBUG_ASSERT(0); + /* Fall through*/ + case 0: + len= 0; + } + g->Message[len]= '\0'; +} + + +FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode) +{ + FILE *f; + if (!(f= fopen(path, mode))) + global_open_error_msg(g, msgid, path, mode); + return f; +} + + +int global_open(GLOBAL *g, int msgid, const char *path, int flags) +{ + int h; + if ((h= open(path, flags)) <= 0) + global_open_error_msg(g, msgid, path, ""); + return h; +} + + +int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode) +{ + int h; + if ((h= open(path, flags, mode)) <= 0) + { + char modestr[64]; + snprintf(modestr, sizeof(modestr), "%d", mode); + global_open_error_msg(g, msgid, path, modestr); + } + return h; +} + + +/**************************************************************************/ +/* Utility for external callers (such as XDB) */ +/**************************************************************************/ +DllExport char *GetIni(int n = 0) + { + switch (n) { + case 1: return plgxini; break; + case 2: return nmfile; break; + case 3: return pdebug; break; + case 4: return version; break; +#if defined(XMSG) + case 5: return msglang; break; +#endif // XMSG +// default: return plgini; + } // endswitch GetIni + + return plgini; + } // end of GetIni + +DllExport void SetTrc(void) + { + // If tracing is on, debug must be initialized. + debug = pfile; + } // end of SetTrc + +#if 0 +/**************************************************************************/ +/* Tracing output function. */ +/**************************************************************************/ +void ptrc(char const *fmt, ...) + { + va_list ap; + va_start (ap, fmt); + +// if (trace == 0 || (trace == 1 && !pfile) || !fmt) +// printf("In %s wrong trace=%d pfile=%p fmt=%p\n", +// __FILE__, trace, pfile, fmt); + + if (trace == 1) + vfprintf(pfile, fmt, ap); + else + vprintf(fmt, ap); + + va_end (ap); + } // end of ptrc +#endif // 0 + +/**************************************************************************/ +/* Allocate the result structure that will contain result data. */ +/**************************************************************************/ +PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids, + int *dbtype, int *buftyp, XFLD *fldtyp, + unsigned int *length, bool blank, bool nonull) + { + char cname[NAM_LEN+1]; + int i; + PCOLRES *pcrp, crp; + PQRYRES qrp; + + /************************************************************************/ + /* Allocate the structure used to contain the result set. */ + /************************************************************************/ + qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES)); + pcrp = &qrp->Colresp; + qrp->Continued = false; + qrp->Truncated = false; + qrp->Info = false; + qrp->Suball = true; + qrp->Maxres = maxres; + qrp->Maxsize = 0; + qrp->Nblin = 0; + qrp->Nbcol = 0; // will be ncol + qrp->Cursor = 0; + qrp->BadLines = 0; + + for (i = 0; i < ncol; i++) { + *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES)); + crp = *pcrp; + pcrp = &crp->Next; + crp->Colp = NULL; + crp->Ncol = ++qrp->Nbcol; + crp->Type = buftyp[i]; + crp->Length = length[i]; + crp->Clen = GetTypeSize(crp->Type, length[i]); + crp->Prec = 0; + crp->DBtype = dbtype[i]; + + if (ids > 0) { +#if defined(XMSG) + // Get header from message file + strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN); + cname[NAM_LEN] = 0; // for truncated long names +//#elif defined(WIN32) + // Get header from ressource file +// LoadString(s_hModule, ids + crp->Ncol, cname, sizeof(cname)); +#else // !WIN32 + GetRcString(ids + crp->Ncol, cname, sizeof(cname)); +#endif // !WIN32 + crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1); + strcpy(crp->Name, cname); + } else + crp->Name = NULL; // Will be set by caller + + if (fldtyp) + crp->Fld = fldtyp[i]; + else + crp->Fld = FLD_NO; + + // Allocate the Value Block that will contain data + if (crp->Length || nonull) + crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres, + crp->Length, 0, true, blank); + else + crp->Kdata = NULL; + + if (g->Trace) + htrc("Column(%d) %s type=%d len=%d value=%p\n", + crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata); + + } // endfor i + + *pcrp = NULL; + + return qrp; + } // end of PlgAllocResult + +/***********************************************************************/ +/* Allocate and initialize the new DB User Block. */ +/***********************************************************************/ +PDBUSER PlgMakeUser(PGLOBAL g) + { + PDBUSER dbuserp; + + if (!(dbuserp = (PDBUSER)PlugAllocMem(g, (uint)sizeof(DBUSERBLK)))) { + sprintf(g->Message, MSG(MALLOC_ERROR), "PlgMakeUser"); + return NULL; + } // endif dbuserp + + memset(dbuserp, 0, sizeof(DBUSERBLK)); +//dbuserp->Act2 = g->Activityp; +//#if defined(UNIX) +// dbuserp->LineLen = 160; +//#else +// dbuserp->LineLen = 78; +//#endif +//dbuserp->Maxres = MAXRES; +//dbuserp->Maxlin = MAXLIN; +//dbuserp->Maxbmp = MAXBMP; +//dbuserp->AlgChoice = AMOD_AUTO; + dbuserp->UseTemp = TMP_AUTO; + dbuserp->Check = CHK_ALL; + strcpy(dbuserp->Server, "CONNECT"); + return dbuserp; + } // end of PlgMakeUser + +/***********************************************************************/ +/* PlgGetUser: returns DBUSER block pointer. */ +/***********************************************************************/ +PDBUSER PlgGetUser(PGLOBAL g) + { + PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL); + + if (!dup) + strcpy(g->Message, MSG(APPL_NOT_INIT)); + + return dup; + } // end of PlgGetUser + +/***********************************************************************/ +/* PlgGetCatalog: returns CATALOG class pointer. */ +/***********************************************************************/ +PCATLG PlgGetCatalog(PGLOBAL g, bool jump) + { + PDBUSER dbuserp = PlgGetUser(g); + PCATLG cat = (dbuserp) ? dbuserp->Catalog : NULL; + + if (!cat && jump) { + // Raise exception so caller doesn't have to check return value + strcpy(g->Message, MSG(NO_ACTIVE_DB)); + longjmp(g->jumper[g->jump_level], 1); + } // endif cat + + return cat; + } // end of PlgGetCatalog + +/***********************************************************************/ +/* PlgGetCatalog: returns CATALOG class pointer. */ +/***********************************************************************/ +char *PlgGetDataPath(PGLOBAL g) + { + PCATLG cat = PlgGetCatalog(g, false); + +//if (!cat) +// return GetIniString(g, NULL, "DataBase", "DataPath", "", plgini); + + return (cat) ? cat->GetDataPath() : NULL; + } // end of PlgGetDataPath + +#if 0 +/***********************************************************************/ +/* PlgGetXdbPath: sets the fully qualified file name of a database */ +/* description file in lgn and the new datapath in dp. */ +/* New database description file is a Configuration Settings file */ +/* that will be used and updated in case of DB modifications such */ +/* as Insert into a VCT file. Look for it and use it if found. */ +/* By default the configuration file is DataPath\name.xdb but the */ +/* configuration file name may also be specified in Plugdb.ini. */ +/***********************************************************************/ +bool PlgSetXdbPath(PGLOBAL g, PSZ dbname, PSZ dbpath, + char *lgn, int lgsize, + char *path, int psize) + { + char *dp, datapath[_MAX_PATH], ft[_MAX_EXT] = ".xdb"; + int n; + + if (path) { + dp = path; + n = psize; + } else { + dp = datapath; + n = sizeof(datapath); + } // endif path + + GetPrivateProfileString("DataBase", "DataPath", "", dp, n, plgini); + + if (trace) + htrc("PlgSetXdbPath: path=%s\n", dp); + + if (dbpath) { + char fn[_MAX_FNAME]; + + strcpy(lgn, dbpath); + _splitpath(lgn, NULL, NULL, fn, NULL); + + if (!*fn) // Old style use command + strcat(lgn, dbname); + + _splitpath(lgn, NULL, NULL, dbname, NULL); // Extract DB name + } else if (strcspn(dbname, ":/\\.") < strlen(dbname)) { + // dbname name contains the path name of the XDB file + strcpy(lgn, dbname); + _splitpath(lgn, NULL, NULL, dbname, NULL); // Extract DB name + } else + /*******************************************************************/ + /* New database description file is a Configuration Settings file */ + /* that will be used and updated in case of DB modifications such */ + /* as Insert into a VCT file. Look for it and use it if found. */ + /* By default the configuration file is DataPath\name.xdb but the */ + /* configuration file name may also be specified in Plugdb.ini. */ + /*******************************************************************/ + GetPrivateProfileString("DBnames", dbname, "", lgn, lgsize, plgini); + + if (*lgn) { +#if !defined(UNIX) + char drive[_MAX_DRIVE]; + char direc[_MAX_DIR]; +#endif + char fname[_MAX_FNAME]; + char ftype[_MAX_EXT]; + + _splitpath(lgn, NULL, NULL, fname, ftype); + + if (!*ftype) + strcat(lgn, ft); + else if (!stricmp(ftype, ".var")) { + strcpy(g->Message, MSG(NO_MORE_VAR)); + return true; + } // endif ftype + + // Given DB description path may be relative to data path + PlugSetPath(lgn, lgn, dp); + + // New data path is the path of the configuration setting file +#if !defined(UNIX) + _splitpath(lgn, drive, direc, NULL, NULL); + _makepath(dp, drive, direc, "", ""); +#else +//#error This must be tested for trailing slash + _splitpath(lgn, NULL, dp, NULL, NULL); +#endif + } else { + // Try dbname[.ext] in the current directory + strcpy(lgn, dbname); + + if (!strchr(dbname, '.')) + strcat(lgn, ft); + + PlugSetPath(lgn, lgn, dp); + } // endif lgn + + if (trace) + htrc("PlgSetXdbPath: new DB description file=%s\n", lgn); + + return false; + } // end of PlgSetXdbPath +#endif // 0 + +/***********************************************************************/ +/* Extract from a path name the required component. */ +/* This function assumes there is enough space in the buffer. */ +/***********************************************************************/ +char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op) + { + char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL; + + switch (op) { // Determine which part to extract +#if !defined(UNIX) + case OP_FDISK: drive = pBuff; break; +#endif // !UNIX + case OP_FPATH: direc = pBuff; break; + case OP_FNAME: fname = pBuff; break; + case OP_FTYPE: ftype = pBuff; break; + default: + sprintf(g->Message, MSG(INVALID_OPER), op, "ExtractFromPath"); + return NULL; + } // endswitch op + + // Now do the extraction + _splitpath(FileName, drive, direc, fname, ftype); + return pBuff; + } // end of PlgExtractFromPath + +/***********************************************************************/ +/* Check the occurence and matching of a pattern against a string. */ +/* Because this function is only used for catalog name checking, */ +/* it must be case insensitive. */ +/***********************************************************************/ +bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat) + { + if (pat && strlen(pat)) { + // This leaves 512 bytes (MAX_STR / 2) for each components + LPSTR name = g->Message + MAX_STR / 2; + + strlwr(strcpy(name, string)); + strlwr(strcpy(g->Message, pat)); // Can be modified by Eval + return EvalLikePattern(name, g->Message); + } else + return true; + + } // end of PlugCheckPattern + +/***********************************************************************/ +/* PlugEvalLike: evaluates a LIKE clause. */ +/* Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */ +/***********************************************************************/ +bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci) + { + char *tp, *sp; + bool b; + + if (trace) + htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat); + + if (ci) { /* Case insensitive test */ + if (strlen(pat) + strlen(strg) + 1 < MAX_STR) + tp = g->Message; + else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) { + strcpy(g->Message, MSG(NEW_RETURN_NULL)); + longjmp(g->jumper[g->jump_level], OP_LIKE); + } /* endif tp */ + + sp = tp + strlen(pat) + 1; + strlwr(strcpy(tp, pat)); /* Make a lower case copy of pat */ + strlwr(strcpy(sp, strg)); /* Make a lower case copy of strg */ + } else { /* Case sensitive test */ + if (strlen(pat) < MAX_STR) /* In most of the case for small pat */ + tp = g->Message; /* Use this as temporary work space. */ + else if (!(tp = new char[strlen(pat) + 1])) { + strcpy(g->Message, MSG(NEW_RETURN_NULL)); + longjmp(g->jumper[g->jump_level], OP_LIKE); + } /* endif tp */ + + strcpy(tp, pat); /* Make a copy to be worked into */ + sp = (char*)strg; + } /* endif ci */ + + b = EvalLikePattern(sp, tp); + + if (tp != g->Message) /* If working space was obtained */ + delete [] tp; /* by the use of new, delete it. */ + + return (b); + } /* end of PlugEvalLike */ + +/***********************************************************************/ +/* M and P are variable length character string. If M and P are zero */ +/* length strings then the Like predicate is true. */ +/* */ +/* The Like predicate is true if: */ +/* */ +/* 1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */ +/* and each <CR> of M is part of exactly one substring. */ +/* */ +/* 2- If the i-th <subtring-specifyer> of P is an <arbitrary-char- */ +/* specifier>, the i-th subtring of M is any single <CR>. */ +/* */ +/* 3- If the i-th <subtring-specifyer> of P is an <arbitrary-string- */ +/* specifier>, then the i-th subtring of M is any sequence of zero */ +/* or more <CR>. */ +/* */ +/* 4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */ +/* character-specifier> nor an <arbitrary-string-specifier>, then */ +/* the i-th substring of M is equal to that <substring-specifier> */ +/* according to the collating sequence of the <like-predicate>, */ +/* without the appending of <space-character>, and has the same */ +/* length as that <substring-specifier>. */ +/* */ +/* 5- The number of substrings of M is equal to the number of */ +/* <subtring-specifiers> of P. */ +/* */ +/* Otherwise M like P is false. */ +/***********************************************************************/ +bool EvalLikePattern(LPCSTR sp, LPCSTR tp) + { + LPSTR p; + char c; + int n; + bool b, t = false; + + if (trace) + htrc("Eval Like: sp=%s tp=%s\n", + (sp) ? sp : "Null", (tp) ? tp : "Null"); + + /********************************************************************/ + /* If pattern is void, Like is true only if string is also void. */ + /********************************************************************/ + if (!*tp) + return (!*sp); + + /********************************************************************/ + /* Analyse eventual arbitrary specifications ahead of pattern. */ + /********************************************************************/ + for (p = (LPSTR)tp; p;) + switch (*p) { /* it can contain % and/or _ */ + case '%': /* An % has been found */ + t = true; /* Note eventual character skip */ + p++; + break; + case '_': /* An _ has been found */ + if (*sp) { /* If more character in string */ + sp++; /* skip it */ + p++; + } else + return false; /* Like condition is not met */ + + break; + default: + tp = p; /* Point to rest of template */ + p = NULL; /* To stop For loop */ + break; + } /* endswitch */ + + if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _ */ + n = p - tp; + else + n = strlen(tp); /* Get length of pattern head */ + + if (trace) + htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p); + + if (n > (signed)strlen(sp)) /* If head is longer than strg */ + b = false; /* Like condition is not met */ + else if (n == 0) /* If void <substring-specifier> */ + b = (t || !*sp); /* true if % or void strg. */ + else if (!t) { + /*******************************************************************/ + /* No character to skip, check occurence of <subtring-specifier> */ + /* at the very beginning of remaining string. */ + /*******************************************************************/ + if (p) { + if ((b = !strncmp(sp, tp, n))) + b = EvalLikePattern(sp + n, p); + + } else + b = !strcmp(sp, tp); /* strg and tmp heads match */ + + } else + if (p) + /*****************************************************************/ + /* Here is the case explaining why we need a recursive routine. */ + /* The test must be done not only against the first occurence */ + /* of the <substring-specifier> in the remaining string, */ + /* but also with all eventual succeeding ones. */ + /*****************************************************************/ + for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) { + *p = '\0'; /* Separate pattern header */ + + if ((sp = strstr(sp, tp))) { + *p = c; + b = EvalLikePattern(sp + n, p); + } else { + *p = c; + b = false; + break; + } /* endif s */ + + } /* endfor b, sp */ + + else { + sp += (strlen(sp) - n); + b = !strcmp(sp, tp); + } /* endif p */ + + if (trace) + htrc(" done: b=%d n=%d sp=%s tp=%s\n", + b, n, (sp) ? sp : "Null", tp); + + return (b); + } /* end of EvalLikePattern */ + +/***********************************************************************/ +/* PlugConvertConstant: convert a Plug constant to an Xobject. */ +/***********************************************************************/ +void PlugConvertConstant(PGLOBAL g, void* & value, short& type) + { + if (trace) + htrc("PlugConvertConstant: value=%p type=%hd\n", value, type); + + if (type != TYPE_XOBJECT) { + value = new(g) CONSTANT(g, value, type); + type = TYPE_XOBJECT; + } // endif type + + } // end of PlugConvertConstant + +/***********************************************************************/ +/* Call the Flex preparser to convert a date format to a sscanf input */ +/* format and a Strftime output format. Flag if not 0 indicates that */ +/* non quoted blanks are not included in the output format. */ +/***********************************************************************/ +PDTP MakeDateFormat(PGLOBAL g, PSZ dfmt, bool in, bool out, int flag) + { + PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR)); + + if (trace) + htrc("MakeDateFormat: dfmt=%s\n", dfmt); + + memset(pdp, 0, sizeof(DATPAR)); + pdp->Format = pdp->Curp = dfmt; + pdp->Outsize = 2 * strlen(dfmt) + 1; + + if (in) + pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize); + + if (out) + pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize); + + pdp->Flag = flag; + + /*********************************************************************/ + /* Call the FLEX generated parser. In multi-threading mode the next */ + /* instruction is included in an Enter/LeaveCriticalSection bracket. */ + /*********************************************************************/ +#if defined(THREAD) +#if defined(WIN32) + EnterCriticalSection((LPCRITICAL_SECTION)&parsec); +#else // !WIN32 + pthread_mutex_lock(&parmut); +#endif // !WIN32 +#endif // THREAD + /*int rc =*/ fmdflex(pdp); +#if defined(THREAD) +#if defined(WIN32) + LeaveCriticalSection((LPCRITICAL_SECTION)&parsec); +#else // !WIN32 + pthread_mutex_unlock(&parmut); +#endif // !WIN32 +#endif // THREAD + + if (trace) + htrc("Done: in=%s out=%s\n", SVP(pdp->InFmt), SVP(pdp->OutFmt)); + return pdp; + } // end of MakeDateFormat + +/***********************************************************************/ +/* Extract the date from a formatted string according to format. */ +/***********************************************************************/ +int ExtractDate(char *dts, PDTP pdp, int defy, int val[6]) + { + char *fmt, c, d, e, W[8][12]; + int i, k, m, numval; + int n, y = 30; + + if (pdp) + fmt = pdp->InFmt; + else // assume standard MySQL date format + fmt = "%4d-%2d-%2d %2d:%2d:%2d"; + + if (trace) + htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy); + + // Set default values for time only use + if (defy) { + // This may be a default value for year + y = defy; + val[0] = y; + y = (y < 100) ? y : 30; + } else + val[0] = 70; + + val[1] = 1; + val[2] = 1; + + for (i = 3; i < 6; i++) + val[i] = 0; + + numval = 0; + + // Get the date field parse it with derived input format + m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]); + + if (m > pdp->Num) + m = pdp->Num; + + for (i = 0; i < m; i++) { + n = *(int*)W[i]; + + switch (k = pdp->Index[i]) { + case 0: + if (n < y) + n += 100; + + val[0] = n; + numval = max(numval, 1); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + val[k] = n; + numval = max(numval, k + 1); + break; + case -1: + c = toupper(W[i][0]); + d = toupper(W[i][1]); + e = toupper(W[i][2]); + + switch (c) { + case 'J': + n = (d == 'A') ? 1 + : (e == 'N') ? 6 : 7; break; + case 'F': n = 2; break; + case 'M': + n = (e == 'R') ? 3 : 5; break; + case 'A': + n = (d == 'P') ? 4 : 8; break; + break; + case 'S': n = 9; break; + case 'O': n = 10; break; + case 'N': n = 11; break; + case 'D': n = 12; break; + } /* endswitch c */ + + val[1] = n; + numval = max(numval, 2); + break; + case -6: + c = toupper(W[i][0]); + n = val[3] % 12; + + if (c == 'P') + n += 12; + + val[3] = n; + break; + } // endswitch Plugpar + + } // endfor i + + if (trace) + htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n", + numval, val[0], val[1], val[2], val[3], val[4], val[5]); + + return numval; + } // end of ExtractDate + +/***********************************************************************/ +/* Open file routine: the purpose of this routine is to make a list */ +/* of all open file so they can be closed in SQLINIT on error jump. */ +/***********************************************************************/ +FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype) + { + FILE *fop; + PFBLOCK fp; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + if (trace) { + htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype); + htrc("dbuserp=%p\n", dbuserp); + } // endif trace + + if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) { + if (trace) + htrc(" fop=%p\n", fop); + + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + + if (trace) + htrc(" fp=%p\n", fp); + + // fname may be in volatile memory such as stack + fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(fname) + 1); + strcpy((char*)fp->Fname, fname); + fp->Count = 1; + fp->Type = TYPE_FB_FILE; + fp->File = fop; + fp->Mode = MODE_ANY; // ??? + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + } /* endif fop */ + + if (trace) + htrc(" returning fop=%p\n", fop); + + return (fop); + } // end of PlugOpenFile + +/***********************************************************************/ +/* Close file routine: the purpose of this routine is to avoid */ +/* double closing that freeze the system on some Unix platforms. */ +/***********************************************************************/ +int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all) + { + int rc = 0; + + if (trace) + htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n", + fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0)); + + if (!fp || !fp->Count) + return rc; + + switch (fp->Type) { + case TYPE_FB_FILE: + if (fclose((FILE *)fp->File) == EOF) + rc = errno; + + fp->File = NULL; + fp->Mode = MODE_ANY; + fp->Count = 0; + break; + case TYPE_FB_MAP: + if ((fp->Count = (all) ? 0 : fp->Count - 1)) + break; + + if (CloseMemMap(fp->Memory, fp->Length)) + rc = (int)GetLastError(); + + fp->Memory = NULL; + fp->Mode = MODE_ANY; + // Passthru + case TYPE_FB_HANDLE: + if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE) + if (CloseFileHandle(fp->Handle)) + rc = (rc) ? rc : (int)GetLastError(); + + fp->Handle = INVALID_HANDLE_VALUE; + fp->Mode = MODE_ANY; + fp->Count = 0; + break; +#ifdef DOMDOC_SUPPORT + case TYPE_FB_XML: + CloseXMLFile(g, fp, all); + break; +#endif // DOMDOC_SUPPORT +#ifdef LIBXML2_SUPPORT + case TYPE_FB_XML2: + CloseXML2File(g, fp, all); + break; +#endif // LIBXML2_SUPPORT + default: + rc = RC_FX; + } // endswitch Type + + return rc; + } // end of PlugCloseFile + +/***********************************************************************/ +/* PlugCleanup: Cleanup remaining items of a SQL query. */ +/***********************************************************************/ +void PlugCleanup(PGLOBAL g, bool dofree) + { + PCATLG cat; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + // The test on Catalog is to avoid a Windows bug that can make + // LoadString in PlugGetMessage to fail in some case + if (!dbuserp || !(cat = dbuserp->Catalog)) + return; + + /*********************************************************************/ + /* Close eventually still open/mapped files. */ + /*********************************************************************/ + for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next) + PlugCloseFile(g, fp, true); + + dbuserp->Openlist = NULL; + + if (dofree) { + /*******************************************************************/ + /* Cleanup any non suballocated memory still not freed. */ + /*******************************************************************/ + for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next) + PlgDBfree(*mp); + + dbuserp->Memlist = NULL; + + /*******************************************************************/ + /* If not using permanent storage catalog, reset volatile values. */ + /*******************************************************************/ + cat->Reset(); + + /*******************************************************************/ + /* This is the place to reset the pointer on domains. */ + /*******************************************************************/ + dbuserp->Subcor = false; + dbuserp->Step = STEP(PARSING_QUERY); + dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0; + } // endif dofree + + } // end of PlugCleanup + +#if 0 +/***********************************************************************/ +/* That stupid Windows 98 does not provide this function. */ +/***********************************************************************/ +bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini) + { + char buf[12]; + + sprintf(buf, "%d", n); + return WritePrivateProfileString(sec, key, buf, ini); + } // end of WritePrivateProfileInt + +/***********************************************************************/ +/* Retrieve a size from an INI file with eventual K or M following. */ +/***********************************************************************/ +int GetIniSize(char *section, char *key, char *def, char *ini) + { + char c, buff[32]; + int i; + int n = 0; + + GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini); + + if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2) + switch (toupper(c)) { + case 'M': + n *= 1024; + case 'K': + n *= 1024; + } // endswitch c + + if (trace) + htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n); + + return n; + } // end of GetIniSize + +/***********************************************************************/ +/* Allocate a string retrieved from an INI file and return its address */ +/***********************************************************************/ +DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key, + LPCSTR def, LPCSTR ini) + { + char buff[_MAX_PATH]; + PSZ p; + int n, m = sizeof(buff); + char *buf = buff; + +#if defined(_DEBUG) + assert (sec && key); +#endif + + again: + n = GetPrivateProfileString(sec, key, def, buf, m, ini); + + if (n == m - 1) { + // String may have been truncated, make sure to have all + if (buf != buff) + delete [] buf; + + m *= 2; + buf = new char[m]; + goto again; + } // endif n + + p = (PSZ)PlugSubAlloc(g, mp, n + 1); + + if (trace) + htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf); + + strcpy(p, buf); + + if (buf != buff) + delete [] buf; + + return p; + } // end of GetIniString +#endif // 0 + +/***********************************************************************/ +/* GetAmName: return the name correponding to an AM code. */ +/***********************************************************************/ +char *GetAmName(PGLOBAL g, AMT am, void *memp) + { + char *amn= (char*)PlugSubAlloc(g, memp, 16); + + switch (am) { + case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break; + case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break; + case TYPE_AM_FILID: strcpy(amn, "FILID"); break; + case TYPE_AM_VIEW: strcpy(amn, "VIEW"); break; + case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break; + case TYPE_AM_DCD: strcpy(amn, "DCD"); break; + case TYPE_AM_CMS: strcpy(amn, "CMS"); break; + case TYPE_AM_MAP: strcpy(amn, "MAP"); break; + case TYPE_AM_FMT: strcpy(amn, "FMT"); break; + case TYPE_AM_CSV: strcpy(amn, "CSV"); break; + case TYPE_AM_MCV: strcpy(amn, "MCV"); break; + case TYPE_AM_DOS: strcpy(amn, "DOS"); break; + case TYPE_AM_FIX: strcpy(amn, "FIX"); break; + case TYPE_AM_BIN: strcpy(amn, "BIN"); break; + case TYPE_AM_VCT: strcpy(amn, "VEC"); break; + case TYPE_AM_VMP: strcpy(amn, "VMP"); break; + case TYPE_AM_DBF: strcpy(amn, "DBF"); break; + case TYPE_AM_QRY: strcpy(amn, "QRY"); break; + case TYPE_AM_SQL: strcpy(amn, "SQL"); break; + case TYPE_AM_PLG: strcpy(amn, "PLG"); break; + case TYPE_AM_PLM: strcpy(amn, "PLM"); break; + case TYPE_AM_DOM: strcpy(amn, "DOM"); break; + case TYPE_AM_DIR: strcpy(amn, "DIR"); break; + case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break; + case TYPE_AM_MAC: strcpy(amn, "MAC"); break; + case TYPE_AM_OEM: strcpy(amn, "OEM"); break; + case TYPE_AM_OUT: strcpy(amn, "OUT"); break; + default: sprintf(amn, "OEM(%d)", am); + } // endswitch am + + return amn; + } // end of GetAmName + +#if defined(WIN32) && !defined(NOCATCH) +/***********************************************************************/ +/* GetExceptionDesc: return the description of an exception code. */ +/***********************************************************************/ +char *GetExceptionDesc(PGLOBAL g, unsigned int e) + { + char *p; + + switch (e) { + case EXCEPTION_GUARD_PAGE: + p = MSG(GUARD_PAGE); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + p = MSG(DATA_MISALIGN); + break; + case EXCEPTION_BREAKPOINT: + p = MSG(BREAKPOINT); + break; + case EXCEPTION_SINGLE_STEP: + p = MSG(SINGLE_STEP); + break; + case EXCEPTION_ACCESS_VIOLATION: + p = MSG(ACCESS_VIOLATN); + break; + case EXCEPTION_IN_PAGE_ERROR: + p = MSG(PAGE_ERROR); + break; + case EXCEPTION_INVALID_HANDLE: + p = MSG(INVALID_HANDLE); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + p = MSG(ILLEGAL_INSTR); + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + p = MSG(NONCONT_EXCEPT); + break; + case EXCEPTION_INVALID_DISPOSITION: + p = MSG(INVALID_DISP); + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + p = MSG(ARRAY_BNDS_EXCD); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + p = MSG(FLT_DENORMAL_OP); + break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + p = MSG(FLT_ZERO_DIVIDE); + break; + case EXCEPTION_FLT_INEXACT_RESULT: + p = MSG(FLT_BAD_RESULT); + break; + case EXCEPTION_FLT_INVALID_OPERATION: + p = MSG(FLT_INVALID_OP); + break; + case EXCEPTION_FLT_OVERFLOW: + p = MSG(FLT_OVERFLOW); + break; + case EXCEPTION_FLT_STACK_CHECK: + p = MSG(FLT_STACK_CHECK); + break; + case EXCEPTION_FLT_UNDERFLOW: + p = MSG(FLT_UNDERFLOW); + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + p = MSG(INT_ZERO_DIVIDE); + break; + case EXCEPTION_INT_OVERFLOW: + p = MSG(INT_OVERFLOW); + break; + case EXCEPTION_PRIV_INSTRUCTION: + p = MSG(PRIV_INSTR); + break; + case EXCEPTION_STACK_OVERFLOW: + p = MSG(STACK_OVERFLOW); + break; + case CONTROL_C_EXIT: + p = MSG(CONTROL_C_EXIT); + break; + case STATUS_NO_MEMORY: + p = MSG(NO_MEMORY); + break; + default: + p = MSG(UNKNOWN_EXCPT); + break; + } // endswitch nSE + + return p; + } // end of GetExceptionDesc +#endif // WIN32 && !NOCATCH + +/***********************************************************************/ +/* PlgDBalloc: allocates or suballocates memory conditionally. */ +/* If mp.Sub is true at entry, this forces suballocation. */ +/* If the memory is allocated, makes an entry in an allocation list */ +/* so it can be freed at the normal or error query completion. */ +/***********************************************************************/ +void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp) + { +//bool b; + size_t maxsub, minsub; + void *arp = (area) ? area : g->Sarea; + PPOOLHEADER pph = (PPOOLHEADER)arp; + + if (mp.Memp) { + // This is a reallocation. If this block is not suballocated, it + // was already placed in the chain of memory blocks and we must + // not do it again as it can trigger a loop when freeing them. + // Note: this works if blocks can be reallocated only once. + // Otherwise a new boolean must be added to the block that + // indicate that it is chained, or a test on the whole chain be + // done to check whether the block is already there. +// b = mp.Sub; + mp.Sub = false; // Restrict suballocation to one quarter + } // endif Memp + + // Suballoc when possible if mp.Sub is initially true, but leaving + // a minimum amount of storage for future operations such as the + // optimize recalculation after insert; otherwise + // suballoc only if size is smaller than one quarter of free mem. + minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2; + maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub; + mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2)); + + if (trace) + htrc("PlgDBalloc: in %p size=%d used=%d free=%d sub=%d\n", + arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub); + + if (!mp.Sub) { + // For allocations greater than one fourth of remaining storage + // in the area, do allocate from virtual storage. +#if defined(WIN32) + if (mp.Size >= BIGMEM) + mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT, PAGE_READWRITE); + else +#endif + mp.Memp = malloc(mp.Size); + + if (!mp.Inlist && mp.Memp) { + // New allocated block, put it in the memory block chain. + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + mp.Next = dbuserp->Memlist; + dbuserp->Memlist = ∓ + mp.Inlist = true; + } // endif mp + + } else + // Suballocating is Ok. + mp.Memp = PlugSubAlloc(g, area, mp.Size); + + return mp.Memp; + } // end of PlgDBalloc + +/***********************************************************************/ +/* PlgDBrealloc: reallocates memory conditionally. */ +/* Note that this routine can fail only when block size is increased */ +/* because otherwise we keep the old storage on failure. */ +/***********************************************************************/ +void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize) + { + MBLOCK m; + +#if defined(_DEBUG) +// assert (mp.Memp != NULL); +#endif + + if (trace) + htrc("PlgDBrealloc: %p size=%d sub=%d\n", mp.Memp, mp.Size, mp.Sub); + + if (newsize == mp.Size) + return mp.Memp; // Nothing to do + else + m = mp; + + if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) { + // Allocation was done by malloc, try to use realloc but + // suballoc if newsize is smaller than one quarter of free mem. + size_t maxsub; + PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea); + + maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072; + + if ((mp.Sub = (newsize <= (maxsub >> 2)))) { + mp.Memp = PlugSubAlloc(g, area, newsize); + memcpy(mp.Memp, m.Memp, min(m.Size, newsize)); + PlgDBfree(m); // Free the old block + } else if (!(mp.Memp = realloc(mp.Memp, newsize))) { + mp = m; // Possible only if newsize > Size + return NULL; // Failed + } // endif's + + mp.Size = newsize; + } else if (!mp.Sub || newsize > mp.Size) { + // Was suballocated or Allocation was done by VirtualAlloc + // Make a new allocation and copy the useful part + // Note: DO NOT reset Memp and Sub so we know that this + // is a reallocation in PlgDBalloc + mp.Size = newsize; + + if (PlgDBalloc(g, area, mp)) { + memcpy(mp.Memp, m.Memp, min(m.Size, newsize)); + PlgDBfree(m); // Free the old block + } else { + mp = m; // No space to realloc, do nothing + + if (newsize > m.Size) + return NULL; // Failed + + } // endif PlgDBalloc + + } // endif's + + if (trace) + htrc(" newsize=%d newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub); + + return mp.Memp; + } // end of PlgDBrealloc + +/***********************************************************************/ +/* PlgDBfree: free memory if not suballocated. */ +/***********************************************************************/ +void PlgDBfree(MBLOCK& mp) + { + if (trace) + htrc("PlgDBfree: %p sub=%d size=%d\n", mp.Memp, mp.Sub, mp.Size); + + if (!mp.Sub && mp.Memp) +#if defined(WIN32) + if (mp.Size >= BIGMEM) + VirtualFree(mp.Memp, 0, MEM_RELEASE); + else +#endif + free(mp.Memp); + + // Do not reset Next to avoid cutting the Mblock chain + mp.Memp = NULL; + mp.Sub = false; + mp.Size = 0; + } // end of PlgDBfree + +#if 0 // Not used yet +/***********************************************************************/ +/* Program for sub-allocating one item in a storage area. */ +/* Note: This function is equivalent to PlugSubAlloc except that in */ +/* case of insufficient memory, it returns NULL instead of doing a */ +/* long jump. The caller must test the return value for error. */ +/***********************************************************************/ +void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size) + { + PPOOLHEADER pph; // Points on area header. + + if (!memp) + /*******************************************************************/ + /* Allocation is to be done in the Sarea. */ + /*******************************************************************/ + memp = g->Sarea; + + size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */ +//size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */ + pph = (PPOOLHEADER)memp; + +#if defined(DEBTRACE) + htrc("PlgDBSubAlloc: memp=%p size=%d used=%d free=%d\n", + memp, size, pph->To_Free, pph->FreeBlk); +#endif + + if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */ + char *pname = NULL; + PACTIVITY ap; + + if (memp == g->Sarea) + pname = "Work"; + else if ((ap = g->Activityp)) { + if (memp == ap->LangRulep) + pname = "Rule"; + else if (memp == ap->Nodep[0]) + pname = "Dictionary"; + else if (memp == ap->Nodep[1]) + pname = "Vartok"; + else if (memp == ap->Nodep[2]) + pname = "Lexicon"; + else if (memp == ap->User_Dictp) + pname = "User dictionary"; + else if (ap->Aptr) + pname = "Application"; + + } // endif memp + + if (pname) + sprintf(g->Message, + "Not enough memory in %s area for request of %d (used=%d free=%d)", + pname, size, pph->To_Free, pph->FreeBlk); + else + sprintf(g->Message, MSG(SUBALLOC_ERROR), + memp, size, pph->To_Free, pph->FreeBlk); + +#if defined(DEBTRACE) + htrc("%s\n", g->Message); +#endif + + return NULL; + } // endif size + + /*********************************************************************/ + /* Do the suballocation the simplest way. */ + /*********************************************************************/ + memp = MakePtr(memp, pph->To_Free); // Points to suballocated block + pph->To_Free += size; // New offset of pool free block + pph->FreeBlk -= size; // New size of pool free block +#if defined(DEBTRACE) + htrc("Done memp=%p used=%d free=%d\n", + memp, pph->To_Free, pph->FreeBlk); +#endif + return (memp); + } // end of PlgDBSubAlloc +#endif // 0 Not used yet + +/***********************************************************************/ +/* PUTOUT: Plug DB object typing routine. */ +/***********************************************************************/ +void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n) + { + char m[64]; + + if (trace) + htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n); + + if (!v) + return; + + memset(m, ' ', n); /* Make margin string */ + m[n] = '\0'; + n += 2; /* Increase margin */ + + switch (t) { + case TYPE_ERROR: + fprintf(f, "--> %s\n", (PSZ)v); + break; + + case TYPE_STRING: + case TYPE_PSZ: + fprintf(f, "%s%s\n", m, (PSZ)v); + break; + + case TYPE_FLOAT: + fprintf(f, "%s%lf\n", m, *(double *)v); + break; + + case TYPE_LIST: + case TYPE_COLIST: + case TYPE_COL: + {PPARM p; + + if (t == TYPE_LIST) + fprintf(f, "%s%s\n", m, MSG(LIST)); + else + fprintf(f, "%s%s\n", m, "Colist:"); + + for (p = (PPARM)v; p; p = p->Next) + PlugPutOut(g, f, p->Type, p->Value, n); + + } break; + + case TYPE_INT: + fprintf(f, "%s%d\n", m, *(int *)v); + break; + + case TYPE_SHORT: + fprintf(f, "%s%hd\n", m, *(short *)v); + break; + + case TYPE_TINY: + fprintf(f, "%s%d\n", m, (int)*(char *)v); + break; + + case TYPE_VOID: + break; + + case TYPE_SQL: + case TYPE_TABLE: + case TYPE_TDB: + case TYPE_XOBJECT: + ((PBLOCK)v)->Print(g, f, n-2); + break; + + default: + fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t); + } /* endswitch */ + + return; + } /* end of PlugPutOut */ + +/***********************************************************************/ +/* NewPointer: makes a table of pointer values to be changed later. */ +/***********************************************************************/ +DllExport void NewPointer(PTABS t, void *oldv, void *newv) + { + PTABPTR tp; + + if (!oldv) /* error ?????????? */ + return; + + if (!t->P1 || t->P1->Num == 50) + { + if (!(tp = new TABPTR)) { + PGLOBAL g = t->G; + + sprintf(g->Message, "NewPointer: %s", MSG(MEM_ALLOC_ERROR)); + longjmp(g->jumper[g->jump_level], 3); + } else { + tp->Next = t->P1; + tp->Num = 0; + t->P1 = tp; + } /* endif tp */ + } + + t->P1->Old[t->P1->Num] = oldv; + t->P1->New[t->P1->Num++] = newv; + } /* end of NewPointer */ + +#if 0 +/***********************************************************************/ +/* Compare two files and return 0 if they are identical, else 1. */ +/***********************************************************************/ +int FileComp(PGLOBAL g, char *file1, char *file2) + { + char *fn[2], *bp[2], buff1[4096], buff2[4096]; + int i, k, n[2], h[2] = {-1,-1}; + int len[2], rc = -1; + + fn[0] = file1; fn[1] = file2; + bp[0] = buff1; bp[1] = buff2; + + for (i = 0; i < 2; i++) { +#if defined(WIN32) + h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY); +#else // !WIN32 + h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY); +#endif // !WIN32 + + if (h[i] == -1) { +// if (errno != ENOENT) { + sprintf(g->Message, MSG(OPEN_MODE_ERROR), + "rb", (int)errno, fn[i]); + strcat(strcat(g->Message, ": "), strerror(errno)); + longjmp(g->jumper[g->jump_level], 666); +// } else +// len[i] = 0; // File does not exist yet + + } else { + if ((len[i] = _filelength(h[i])) < 0) { + sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn[i]); + longjmp(g->jumper[g->jump_level], 666); + } // endif len + + } // endif h + + } // endfor i + + if (len[0] != len[1]) + rc = 1; + + while (rc == -1) { + for (i = 0; i < 2; i++) + if ((n[i] = read(h[i], bp[i], 4096)) < 0) { + sprintf(g->Message, MSG(READ_ERROR), fn[i], strerror(errno)); + goto fin; + } // endif n + + if (n[0] != n[1]) + rc = 1; + else if (*n == 0) + rc = 0; + else for (k = 0; k < *n; k++) + if (*(bp[0] + k) != *(bp[1] + k)) { + rc = 1; + goto fin; + } // endif bp + + } // endwhile + + fin: + for (i = 0; i < 2; i++) + if (h[i] != -1) + close(h[i]); + + return rc; + } // end of FileComp +#endif // 0 diff --git a/storage/connect/plgodbc.h b/storage/connect/plgodbc.h new file mode 100644 index 00000000000..46bc41ad16b --- /dev/null +++ b/storage/connect/plgodbc.h @@ -0,0 +1,230 @@ +/***********************************************************************/ +/* PLGODBC.H - This is the ODBC PlugDB driver include file. */ +/***********************************************************************/ +//efine WINVER 0x0300 // prevent Windows 3.1 feature usage +#include <windows.h> /* Windows include file */ +#include <windowsx.h> /* Message crackers */ +#include <commctrl.h> + +/***********************************************************************/ +/* Included C-definition files required by the interface. */ +/***********************************************************************/ +#include <string.h> /* String manipulation declares */ +#include <stdlib.h> /* C standard library */ +#include <ctype.h> /* C language specific types */ +#include <stdio.h> /* FOPEN_MAX declaration */ +#include <time.h> /* time_t type declaration */ + +/***********************************************************************/ +/* ODBC interface of PlugDB driver declares. */ +/***********************************************************************/ +#include "podbcerr.h" /* Resource ID for PlugDB Driver */ +#if !defined(WIN32) +#include "w16macro.h" +#endif + +#define ODBCVER 0x0300 + +#include "sqltypes.h" +#include "sql.h" +#include "sqlext.h" + +/***********************************************************************/ +/* Definitions to be used in function prototypes. */ +/* The SQL_API is to be used only for those functions exported for */ +/* driver manager use. */ +/* The EXPFUNC is to be used only for those functions exported but */ +/* used internally, ie, dialog procs. */ +/* The INTFUNC is to be used for all other functions. */ +/***********************************************************************/ +#if defined(WIN32) +#define INTFUNC __stdcall +#define EXPFUNC __stdcall +#else +#define INTFUNC FAR PASCAL +#define EXPFUNC __export CALLBACK +#endif + +/***********************************************************************/ +/* External variables. */ +/***********************************************************************/ +extern HINSTANCE NEAR s_hModule; // DLL handle. +#ifdef DEBTRACE +extern FILE *debug; +#endif +extern bool clearerror; + +/***********************************************************************/ +/* Additional values used by PlugDB for ODBC. */ +/***********************************************************************/ +#define RES_TYPE_PREPARE 1 /* Result from SQLPrepare */ +#define RES_TYPE_CATALOG 2 /* Result from catalog funcs */ +#define MAXPATHLEN _MAX_PATH /* Max path length */ +#define MAXKEYLEN 16 /* Max keyword length */ +#define MAXDESC 256 /* Max description length */ +#define MAXDSNAME 33 /* Max data source name length */ +#define MAXCRNAME 18 /* Max stmt cursor name length */ +#define DEFMAXRES 6300 /* Default MaxRes value */ +#define NAM_LEN 128 /* Length of col and tab names */ + +#define MAXRESULT 1000 /* ? */ +#define MAXCOMMAND 200 /* ? */ +#define RC_ERROR RC_LAST-1 +#define RC_FREE 3 + +#if !defined(NOLIB) +#define CNXKEY uint /* C Key returned by Conn DLL */ +#else +typedef struct _conninfo *PCONN; +#endif /* !NOLIB */ + +#if defined(DEBTRACE) +#define TRACE0(X) fprintf(debug,X); +#define TRACE1(X,A) fprintf(debug,X,A); +#define TRACE2(X,A,B) fprintf(debug,X,A,B); +#define TRACE3(X,A,B,C) fprintf(debug,X,A,B,C); +#define TRACE4(X,A,B,C,D) fprintf(debug,X,A,B,C,D); +#define TRACE5(X,A,B,C,D,E) fprintf(debug,X,A,B,C,D,E); +#define TRACE6(X,A,B,C,D,E,F) fprintf(debug,X,A,B,C,D,E,F); +#else /* !DEBTRACE*/ +#define TRACE0(X) +#define TRACE1(X,A) +#define TRACE2(X,A,B) +#define TRACE3(X,A,B,C) +#define TRACE4(X,A,B,C,D) +#define TRACE5(X,A,B,C,D,E) +#define TRACE6(X,A,B,C,D,E,F) +#endif /* !DEBTRACE*/ + +// This definition MUST be identical to the value in plgdbsem.h +#define XMOD_PREPARE 1 + +/***********************************************************************/ +/* ODBC.INI keywords (use extern definition in SETUP.C) */ +/***********************************************************************/ +extern char const *EMPTYSTR; /* Empty String */ +extern char const *OPTIONON; +extern char const *OPTIONOFF; +extern char const *INI_SDEFAULT; /* Default data source name */ +extern char const *ODBC_INI; /* ODBC initialization file */ +extern char const *INI_KDEFL; /* Is SQL to use by default? */ +extern char const *INI_KLANG; /* Application language */ +extern char const *INI_KDATA; /* Data description file */ +extern char const *INI_KSVR; /* PLG Server */ + +/************************************************************************/ +/* Attribute key indexes (into an array of Attr structs, see below) */ +/************************************************************************/ +#define KEY_DSN 0 +#define KEY_DEFL 1 +#define KEY_LANG 2 +#define KEY_DATA 3 +#define KEY_SERVER 4 +#define LAST_KEY 5 /* Number of keys in TAG's */ +#define KEY_DESC 5 +#define KEY_TRANSNAME 6 +#define KEY_TRANSOPTION 7 +#define KEY_TRANSDLL 8 +#define NUMOFKEYS 9 /* Number of keys supported */ + +#define FOURYEARS 126230400 // Four years in seconds (1 leap) + +/***********************************************************************/ +/* This is used when an "out of memory" error happens, because this */ +/* error recording system allocates memory when it logs an error, */ +/* and it would be bad if it tried to allocate memory when it got an */ +/* out of memory error. */ +/***********************************************************************/ +typedef enum _ERRSTAT { + errstatOK, + errstatNO_MEMORY, + } ERRSTAT; + +/***********************************************************************/ +/* Types */ +/***********************************************************************/ +typedef struct TagAttr { + bool fSupplied; + char Attr[MAXPATHLEN]; + } TAG, *PTAG; + +typedef struct _parscons { /* Parse constants */ + int Slen; /* String length */ + int Ntag; /* Number of entries in tags */ + int Nlook; /* Number of entries in lookup */ + char Sep; /* Separator */ + } PARC, *PPARC; + +/***********************************************************************/ +/* Attribute string look-up table (maps keys to associated indexes) */ +/***********************************************************************/ +typedef struct _Look { + const char *szKey; + int iKey; + } LOOK, *PLOOK; + +/***********************************************************************/ +/* This is info about a single error. */ +/***********************************************************************/ +typedef struct _ERRBLK *PERRBLK; + +typedef struct _ERRBLK { + PERRBLK Next; /* Next block in linked list of error blocks */ + DWORD Native_Error; /* Native error */ + DWORD Stderr; /* SQLC error code */ + PSZ Message; /* Points to text of message */ + } ERRBLK; + +/***********************************************************************/ +/* This is a header block, it records information about a list of */ +/* errors (ERRBLOCK's). */ +/***********************************************************************/ +typedef struct _ERRINFO { + PERRBLK First; /* First block in linked list of error blocks. */ + PERRBLK Last; /* Last block in above list. */ + ERRSTAT Errstat; /* Status for special condition out of memory */ + } ERRINFO, *PERRINFO; + +/***********************************************************************/ +/* Environment information. */ +/***********************************************************************/ +typedef struct _env { + ERRINFO Errinfo; /* Error list */ + UDWORD ODBCver; + UDWORD ODBCdateformat; + } ENV, *PENV; + +/***********************************************************************/ +/* Classes used in the PlugDB ODBC Driver. */ +/***********************************************************************/ +typedef class DBC *PDBC; +typedef class STMT *PSTMT; +typedef class CURSOR *PCURSOR; +typedef class RESULT *PRESULT; +typedef class BINDDATA *PBIND; +typedef class BINDPARM *PBDPARM; +typedef class CPLGdrv *PSCDRV; +typedef class DESCRIPTOR *PDSC; + +/***********************************************************************/ +/* ODBC Prototypes. */ +/***********************************************************************/ +void PostSQLError(HENV, HDBC, HSTMT, DWORD, DWORD, PSZ); +void ClearSQLError(HENV, HDBC, HSTMT); +short LoadRcString(UWORD, LPSTR, short); +RETCODE RetcodeCopyBytes(HDBC, HSTMT, UCHAR FAR *, SWORD, + SWORD FAR *, UCHAR FAR *, SWORD, bool); + +/***********************************************************************/ +/* Private functions used by the driver. */ +/***********************************************************************/ +bool EXPFUNC FDriverConnectProc(HWND, WORD, WPARAM, LPARAM); +extern void ParseAttrString(PLOOK, PTAG, UCHAR FAR *, PPARC); +RETCODE PASCAL StringCopy(HDBC, HSTMT, PTR, SWORD, SWORD FAR *, + char FAR *); +RETCODE PASCAL ShortCopy(HDBC, HSTMT, PTR, SWORD, SWORD FAR *, short); +RETCODE PASCAL LongCopy(HDBC, HSTMT, PTR, SWORD, SWORD FAR *, int); +RETCODE PASCAL GeneralCopy(HDBC, HSTMT, PTR, SWORD, + SWORD FAR *, PTR, SWORD); + +/* --------------------- End of PLGODBC.H ---------------------------- */ diff --git a/storage/connect/plgxml.cpp b/storage/connect/plgxml.cpp new file mode 100644 index 00000000000..3061a6d697e --- /dev/null +++ b/storage/connect/plgxml.cpp @@ -0,0 +1,140 @@ +/******************************************************************/ +/* Implementation of XML document processing using PdbXML. */ +/* Author: Olivier Bertrand 2007-2012 */ +/******************************************************************/ +#include "my_global.h" +#include "global.h" +#include "plgdbsem.h" +#include "block.h" +#include "plgxml.h" + +#if !defined(DOMDOC_SUPPORT) +PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + strcpy(g->Message, MSG(DOM_NOT_SUPP)); + return NULL; + } // end of GetDomDoc +#endif // !DOMDOC_SUPPORT + +#ifndef LIBXML2_SUPPORT +PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp) + { + strcpy(g->Message, "libxml2 not supported"); + return NULL; + } // end of GetLibxmlDoc +#endif // LIBXML2_SUPPORT + +/******************************************************************/ +/* XMLDOCUMENT constructor. */ +/******************************************************************/ +XMLDOCUMENT::XMLDOCUMENT(char *nsl, char *nsdf, char *enc) + { + Namespaces = NULL; + Encoding = enc; + Nslist = nsl; + DefNs = nsdf; + } // end of XMLDOCUMENT constructor + +/******************************************************************/ +/* Make the namespace structure list. */ +/******************************************************************/ +bool XMLDOCUMENT::MakeNSlist(PGLOBAL g) + { + char *prefix, *href, *next = Nslist; + PNS nsp, *ppns = &Namespaces; + + while (next) { + // Skip spaces + while ((*next) == ' ') + next++; + + if ((*next) == '\0') + break; + + // Find prefix + prefix = next; + next = strchr(next, '='); + + if (next == NULL) { + strcpy(g->Message, MSG(BAS_NS_LIST)); + return true; + } // endif next + + *(next++) = '\0'; + + // Find href + href = next; + next = strchr(next, ' '); + + if (next != NULL) + *(next++) = '\0'; + + // Allocate and link NS structure + nsp = (PNS)PlugSubAlloc(g, NULL, sizeof(NS)); + nsp->Next = NULL; + nsp->Prefix = prefix; + nsp->Uri = href; + *ppns = nsp; + ppns = &nsp->Next; + } // endwhile next + + return false; + } // end of MakeNSlist + +/******************************************************************/ +/* XMLNODE constructor. */ +/******************************************************************/ +XMLNODE::XMLNODE(PXDOC dp) + { + Doc = dp; + Next = NULL; + Children = NULL; + Buf = NULL; + Len = -1; + } // end of XMLNODE constructor + +/******************************************************************/ +/* Attach new node at the end of this node children list. */ +/******************************************************************/ +PXNODE XMLNODE::NewChild(PXNODE ncp) +{ + PXNODE np, *pnp = &Children; + + for (np = *pnp; np; np = np->Next) + pnp = &np->Next; + + *pnp = np; + return ncp; +} // end of NewChild + +/******************************************************************/ +/* Delete a node from this node children list. */ +/******************************************************************/ +void XMLNODE::Delete(PXNODE dnp) + { + PXNODE *pnp = &Children; + + for (PXNODE np = *pnp; np; np = np->Next) + if (np == dnp) { + *pnp = dnp->Next; + break; + } else + pnp = &np->Next; + + } // end of Delete + +/******************************************************************/ +/* Store a string in Buf, enventually reallocating it. */ +/******************************************************************/ +char *XMLNODE::BufAlloc(PGLOBAL g, char *p, int n) + { + if (Len < n) { + Len = n; + Buf = (char*)PlugSubAlloc(g, NULL, n + 1); + } // endif Len + + *Buf = '\0'; + return strncat(Buf, p, n); + } // end of BufAlloc diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h new file mode 100644 index 00000000000..8fc95eabef7 --- /dev/null +++ b/storage/connect/plgxml.h @@ -0,0 +1,177 @@ +/******************************************************************/ +/* Dual XML implementation base classes defines. */ +/******************************************************************/ +#if !defined(BASE_BUFFER_SIZE) +enum ElementType { // libxml2 + XML_ELEMENT_NODE = 1, + XML_ATTRIBUTE_NODE = 2, + XML_TEXT_NODE = 3, + XML_CDATA_SECTION_NODE = 4, + XML_ENTITY_REF_NODE = 5, + XML_ENTITY_NODE = 6, + XML_PI_NODE = 7, + XML_COMMENT_NODE = 8, + XML_DOCUMENT_NODE = 9, + XML_DOCUMENT_TYPE_NODE = 10, + XML_DOCUMENT_FRAG_NODE = 11, + XML_NOTATION_NODE = 12, + XML_HTML_DOCUMENT_NODE = 13, + XML_DTD_NODE = 14, + XML_ELEMENT_DECL = 15, + XML_ATTRIBUTE_DECL = 16, + XML_ENTITY_DECL = 17, + XML_NAMESPACE_DECL = 18, + XML_XINCLUDE_START = 19, + XML_XINCLUDE_END = 20, + XML_DOCB_DOCUMENT_NODE = 21}; +#endif // !BASE_BUFFER_SIZE + +//#if !defined(NODE_TYPE_LIST) +#ifdef NOT_USED +enum NodeType { // MS DOM + NODE_ELEMENT = 1, + NODE_ATTRIBUTE = 2, + NODE_TEXT = 3, + NODE_CDATA_SECTION = 4, + NODE_ENTITY_REFERENCE = 5, + NODE_ENTITY = 6, + NODE_PROCESSING_INSTRUCTION = 7, + NODE_COMMENT = 8, + NODE_DOCUMENT = 9, + NODE_DOCUMENT_TYPE = 10, + NODE_DOCUMENT_FRAGMENT = 11, + NODE_NOTATION = 12}; +#endif // !NODE_TYPE_LIST + +typedef class XMLDOCUMENT *PXDOC; // Document +typedef class XMLNODE *PXNODE; // Node (Element) +typedef class XMLNODELIST *PXLIST; // Node list +typedef class XMLATTRIBUTE *PXATTR; // Attribute + +typedef struct _ns { + struct _ns *Next; + char *Prefix; + char *Uri; + } NS, *PNS; + +PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp = NULL); +PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf, + char *enc, PFBLOCK fp = NULL); + +/******************************************************************/ +/* Declaration of XML document. */ +/******************************************************************/ +class XMLDOCUMENT : public BLOCK { + friend class XML2NODE; + friend class DOMNODE; + public: + // Properties + virtual short GetDocType(void) = 0; + virtual void *GetDocPtr(void) = 0; + + // Methods + virtual bool Initialize(PGLOBAL) = 0; + virtual bool ParseFile(char *) = 0; + virtual bool NewDoc(PGLOBAL, char *) = 0; + virtual void AddComment(PGLOBAL, char *) = 0; + virtual PXNODE GetRoot(PGLOBAL) = 0; + virtual PXNODE NewRoot(PGLOBAL, char *) = 0; + virtual PXNODE NewPnode(PGLOBAL, char * = NULL) = 0; + virtual PXATTR NewPattr(PGLOBAL) = 0; + virtual PXLIST NewPlist(PGLOBAL) = 0; + virtual int DumpDoc(PGLOBAL, char *) = 0; + virtual void CloseDoc(PGLOBAL, PFBLOCK) = 0; + virtual PFBLOCK LinkXblock(PGLOBAL, MODE, int, char *) = 0; + + protected: + // Constructor + XMLDOCUMENT(char *nsl, char *nsdf, char *enc); + + // Utility + bool MakeNSlist(PGLOBAL g); + + // Members + PNS Namespaces; /* To the namespaces */ + char *Encoding; /* The document encoding */ + char *Nslist; /* Namespace list */ + char *DefNs; /* Default namespace */ +}; // end of class XMLDOCUMENT + +/******************************************************************/ +/* Declaration of XML node. */ +/******************************************************************/ +class XMLNODE : public BLOCK { + public: + // Properties + virtual char *GetName(PGLOBAL) = 0; + virtual int GetType(void) = 0; + virtual PXNODE GetNext(PGLOBAL) = 0; + virtual PXNODE GetChild(PGLOBAL) = 0; + + // Methods + virtual RCODE GetContent(PGLOBAL, char *, int) = 0; + virtual bool SetContent(PGLOBAL, char *, int) = 0; + virtual PXNODE Clone(PGLOBAL, PXNODE) = 0; + virtual PXLIST GetChildElements(PGLOBAL, char * = NULL, PXLIST = NULL) = 0; + virtual PXLIST SelectNodes(PGLOBAL, char *, PXLIST = NULL) = 0; + virtual PXNODE SelectSingleNode(PGLOBAL, char *, PXNODE = NULL) = 0; + virtual PXATTR GetAttribute(PGLOBAL, char *, PXATTR = NULL) = 0; + virtual PXNODE AddChildNode(PGLOBAL, char *, PXNODE = NULL) = 0; + virtual PXATTR AddProperty(PGLOBAL, char *, PXATTR = NULL) = 0; + virtual void AddText(PGLOBAL, char *) = 0; + virtual void DeleteChild(PGLOBAL, PXNODE) = 0; + + protected: + PXNODE NewChild(PXNODE ncp); + void Delete(PXNODE dnp); + char *BufAlloc(PGLOBAL g, char *p, int n); + + // Constructor + XMLNODE(PXDOC dp); + + // Members + PXDOC Doc; + PXNODE Next; + PXNODE Children; + char *Buf; + int Len; +}; // end of class XMLNODE + +/******************************************************************/ +/* Declaration of XML node list. */ +/******************************************************************/ +class XMLNODELIST : public BLOCK { + public: + // Properties + virtual int GetLength(void) = 0; + virtual PXNODE GetItem(PGLOBAL, int, PXNODE = NULL) = 0; + + protected: + // Constructor + XMLNODELIST(PXDOC dp) {Doc = dp;} + + // Members + PXDOC Doc; +}; // end of class XMLNODELIST + +/******************************************************************/ +/* Declaration of XML attribute. */ +/******************************************************************/ +class XMLATTRIBUTE : public BLOCK { + public: + // Properties +//virtual char *GetText(void) = 0; + + // Methods + virtual bool SetText(PGLOBAL, char *, int) = 0; + + protected: + // Constructor + XMLATTRIBUTE(PXDOC dp) {Doc = dp;} + + // Members + PXDOC Doc; +}; // end of class XMLATTRIBUTE + + diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c new file mode 100644 index 00000000000..693ae96cf52 --- /dev/null +++ b/storage/connect/plugutil.c @@ -0,0 +1,499 @@ +/************** PlugUtil C Program Source Code File (.C) ***************/ +/* */ +/* PROGRAM NAME: PLUGUTIL */ +/* ------------- */ +/* Version 2.7 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1993-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are initialization and utility Plug routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* See Readme.C for a list and description of required SYSTEM files. */ +/* */ +/* PLUG.C - Source code */ +/* GLOBAL.H - Global declaration file */ +/* OPTION.H - Option declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* */ +/* OS2.LIB - OS2 libray */ +/* LLIBCE.LIB - Protect mode/standard combined large model C */ +/* library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* */ +/* IBM C Compiler */ +/* IBM Linker */ +/* */ +/***********************************************************************/ +//efine DEBTRACE 3 +//efine DEBTRACE2 + +/***********************************************************************/ +/* */ +/* Include relevant MariaDB header file. */ +/* */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//#include <windows.h> +#else +#if defined(UNIX) || defined(UNIV_LINUX) +#include <errno.h> +#include <unistd.h> +//#define __stdcall +#else +#include <dir.h> +#endif +#include <stdarg.h> +#endif + +#if defined(WIN) +#include <alloc.h> +#endif +#include <errno.h> /* definitions of ERANGE ENOMEM */ +#if !defined(UNIX) && !defined(UNIV_LINUX) +#include <direct.h> /* Directory management library */ +#endif + +/***********************************************************************/ +/* */ +/* Include application header files */ +/* */ +/* global.h is header containing all global declarations. */ +/* */ +/***********************************************************************/ +#define STORAGE /* Initialize global variables */ + +#include "osutil.h" +#include "global.h" + +#if defined(WIN32) +extern HINSTANCE s_hModule; /* Saved module handle */ +#endif // WIN32 + +extern char plgini[]; +extern int trace; + +#if defined(XMSG) +extern char msglang[]; +#endif // XMSG + +/***********************************************************************/ +/* Local Definitions and static variables */ +/***********************************************************************/ +typedef struct { + ushort Segsize; + ushort Size; + } AREASIZE; + +ACTIVITY defActivity = { /* Describes activity and language */ + NULL, /* Points to user work area(s) */ + "Unknown"}; /* Application name */ + +#if defined(XMSG) || defined(NEWMSG) + static char stmsg[200]; +#endif // XMSG || NEWMSG + +#if defined(UNIX) || defined(UNIV_LINUX) +int GetRcString(int id, char *buf, int bufsize); +#endif // UNIX + +/**************************************************************************/ +/* Tracing output function. */ +/**************************************************************************/ +void htrc(char const *fmt, ...) + { + va_list ap; + va_start (ap, fmt); + +//if (trace == 0 || (trace == 1 && !debug) || !fmt) { +// printf("In %s wrong trace=%d debug=%p fmt=%p\n", +// __FILE__, trace, debug, fmt); +// trace = 0; +// } // endif trace + +//if (trace == 1) +// vfprintf(debug, fmt, ap); +//else + vfprintf(stderr, fmt, ap); + + va_end (ap); + } // end of htrc + +/***********************************************************************/ +/* Plug initialization routine. */ +/* Language points on initial language name and eventual path. */ +/* Return value is the pointer to the Global structure. */ +/***********************************************************************/ +PGLOBAL PlugInit(LPCSTR Language, uint worksize) + { + PGLOBAL g; + + if (trace > 1) + htrc("PlugInit: Language='%s'\n", + ((!Language) ? "Null" : (char*)Language)); + + if (!(g = malloc(sizeof(GLOBAL)))) { + fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL)); + } else { + g->Sarea_Size = worksize; + g->Trace = 0; + g->Createas = 0; + g->Activityp = g->ActivityStart = NULL; + g->Xchk = NULL; + strcpy(g->Message, ""); + + /*******************************************************************/ + /* Allocate the main work segment. */ + /*******************************************************************/ + if (!(g->Sarea = PlugAllocMem(g, worksize))) { + char errmsg[256]; + sprintf(errmsg, MSG(WORK_AREA), g->Message); + strcpy(g->Message, errmsg); + } /* endif Sarea */ + + } /* endif g */ + + g->jump_level = -1; /* New setting to allow recursive call of Plug */ + return(g); + } /* end of PlugInit */ + +/***********************************************************************/ +/* PlugExit: Terminate Plug operations. */ +/***********************************************************************/ +int PlugExit(PGLOBAL g) + { + int rc = 0; + + if (!g) + return rc; + + free(g->Sarea); + free(g); + return rc; + } /* end of PlugExit */ + +/***********************************************************************/ +/* Remove the file type from a file name. */ +/* Note: this routine is not really implemented for Unix. */ +/***********************************************************************/ +LPSTR PlugRemoveType(LPSTR pBuff, LPCSTR FileName) + { +#if !defined(UNIX) && !defined(UNIV_LINUX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ftype[_MAX_EXT]; + + _splitpath(FileName, drive, direc, fname, ftype); + + if (trace > 1) { + htrc("after _splitpath: FileName=%s\n", FileName); + htrc("drive=%s dir=%s fname=%s ext=%s\n", + SVP(drive), direc, fname, ftype); + } // endif trace + + _makepath(pBuff, drive, direc, fname, ""); + + if (trace > 1) + htrc("buff='%s'\n", pBuff); + + return pBuff; + } // end of PlugRemoveType + +/***********************************************************************/ +/* Set the full path of a file relatively to a given path. */ +/* Note: this routine is not really implemented for Unix. */ +/***********************************************************************/ +LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR FileName, LPCSTR defpath) + { + char newname[_MAX_PATH]; + char direc[_MAX_DIR], defdir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ftype[_MAX_EXT]; +#if !defined(UNIX) && !defined(UNIV_LINUX) + char drive[_MAX_DRIVE], defdrv[_MAX_DRIVE]; +#else + char *drive = NULL, *defdrv = NULL; +#endif + + if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) { + strcpy(pBuff, FileName); // Remote file + return pBuff; + } // endif + + _splitpath(FileName, drive, direc, fname, ftype); + _splitpath(defpath, defdrv, defdir, NULL, NULL); + + if (trace > 1) { + htrc("after _splitpath: FileName=%s\n", FileName); +#if defined(UNIX) || defined(UNIV_LINUX) + htrc("dir=%s fname=%s ext=%s\n", direc, fname, ftype); +#else + htrc("drive=%s dir=%s fname=%s ext=%s\n", drive, direc, fname, ftype); + htrc("defdrv=%s defdir=%s\n", defdrv, defdir); +#endif + } // endif trace + + if (drive && !*drive) + strcpy(drive, defdrv); + + switch (*direc) { + case '\0': + strcpy(direc, defdir); + break; + case '\\': + case '/': + break; + default: + // This supposes that defdir ends with a SLASH + strcpy(direc, strcat(defdir, direc)); + } // endswitch + + _makepath(newname, drive, direc, fname, ftype); + + if (trace > 1) + htrc("newname='%s'\n", newname); + + if (_fullpath(pBuff, newname, _MAX_PATH)) { + if (trace > 1) + htrc("pbuff='%s'\n", pBuff); + + return pBuff; + } else + return FileName; // Error, return unchanged name + + } // end of PlugSetPath + +#if defined(XMSG) +/***********************************************************************/ +/* PlugGetMessage: get a message from the message file. */ +/***********************************************************************/ +char *PlugReadMessage(PGLOBAL g, int mid, char *m) + { + char msgfile[_MAX_PATH], msgid[32], buff[256]; + char *msg; + FILE *mfile = NULL; + + GetPrivateProfileString("Message", msglang, "Message\\english.msg", + msgfile, _MAX_PATH, plgini); + + if (!(mfile = fopen(msgfile, "rt"))) { + sprintf(stmsg, "Fail to open message file %s for %s", msgfile, msglang); + goto err; + } // endif mfile + + for (;;) + if (!fgets(buff, 256, mfile)) { + sprintf(stmsg, "Cannot get message %d %s", mid, SVP(m)); + goto fin; + } else + if (atoi(buff) == mid) + break; + + if (sscanf(buff, " %*d %s \"%[^\"]", msgid, stmsg) < 2) { + // Old message file + if (!sscanf(buff, " %*d \"%[^\"]", stmsg)) { + sprintf(stmsg, "Bad message file for %d %s", mid, SVP(m)); + goto fin; + } else + m = NULL; + + } // endif sscanf + + if (m && strcmp(m, msgid)) { + // Message file is out of date + strcpy(stmsg, m); + goto fin; + } // endif m + + fin: + fclose(mfile); + + err: + if (g) { + // Called by STEP + msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1); + strcpy(msg, stmsg); + } else // Called by MSG or PlgGetErrorMsg + msg = stmsg; + + return msg; + } // end of PlugReadMessage + +#elif defined(NEWMSG) +/***********************************************************************/ +/* PlugGetMessage: get a message from the resource string table. */ +/***********************************************************************/ +char *PlugGetMessage(PGLOBAL g, int mid) + { + char *msg; + +#if !defined(UNIX) && !defined(UNIV_LINUX) + int n = LoadString(s_hModule, (uint)mid, (LPTSTR)stmsg, 200); + + if (n == 0) { + DWORD rc = GetLastError(); + msg = (char*)PlugSubAlloc(g, NULL, 512); // Extend buf allocation + n = sprintf(msg, "Message %d, rc=%d: ", mid, rc); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)(msg + n), 512 - n, NULL); + return msg; + } // endif n + +#else // UNIX + if (!GetRcString(mid, stmsg, 200)) + sprintf(stmsg, "Message %d not found", mid); +#endif // UNIX + + if (g) { + // Called by STEP + msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1); + strcpy(msg, stmsg); + } else // Called by MSG or PlgGetErrorMsg + msg = stmsg; + + return msg; + } // end of PlugGetMessage +#endif // NEWMSG + +#if defined(WIN32) +/***********************************************************************/ +/* Return the line length of the console screen buffer. */ +/***********************************************************************/ +short GetLineLength(PGLOBAL g) + { + CONSOLE_SCREEN_BUFFER_INFO coninfo; + HANDLE hcons = GetStdHandle(STD_OUTPUT_HANDLE); + BOOL b = GetConsoleScreenBufferInfo(hcons, &coninfo); + + return (b) ? coninfo.dwSize.X : 0; + } // end of GetLineLength +#endif // WIN32 + +/***********************************************************************/ +/* Program for memory allocation of work and language areas. */ +/***********************************************************************/ +void *PlugAllocMem(PGLOBAL g, uint size) + { + void *areap; /* Pointer to allocated area */ + + /*********************************************************************/ + /* This is the allocation routine for the WIN32/UNIX/AIX version. */ + /*********************************************************************/ + if (!(areap = malloc(size))) + sprintf(g->Message, MSG(MALLOC_ERROR), "malloc"); + + if (trace > 1) { + if (areap) + htrc("Memory of %u allocated at %p\n", size, areap); + else + htrc("PlugAllocMem: %s\n", g->Message); + + } // endif trace + + return (areap); + } /* end of PlugAllocMem */ + +/***********************************************************************/ +/* Program for SubSet initialization of memory pools. */ +/* Here there should be some verification done such as validity of */ +/* the address and size not larger than memory size. */ +/***********************************************************************/ +BOOL PlugSubSet(PGLOBAL g, void *memp, uint size) + { + PPOOLHEADER pph = memp; + + pph->To_Free = (OFFSET)sizeof(POOLHEADER); + pph->FreeBlk = size - pph->To_Free; + + return FALSE; + } /* end of PlugSubSet */ + +/***********************************************************************/ +/* Program for sub-allocating one item in a storage area. */ +/* Note: SubAlloc routines of OS/2 are no more used to increase the */ +/* code portability and avoid problems when a grammar compiled under */ +/* one version of OS/2 is used under another version. */ +/* The simple way things are done here is also based on the fact */ +/* that no freeing of suballocated blocks is permitted in Plug. */ +/***********************************************************************/ +void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size) + { + PPOOLHEADER pph; /* Points on area header. */ + + if (!memp) + /*******************************************************************/ + /* Allocation is to be done in the Sarea. */ + /*******************************************************************/ + memp = g->Sarea; + +//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */ + size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */ + pph = (PPOOLHEADER)memp; + +#if defined(DEBUG2) || defined(DEBUG3) + htrc("SubAlloc in %p size=%d used=%d free=%d\n", + memp, size, pph->To_Free, pph->FreeBlk); +#endif + + if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */ + char *pname = "Work"; + + sprintf(g->Message, + "Not enough memory in %s area for request of %u (used=%d free=%d)", + pname, (uint) size, pph->To_Free, pph->FreeBlk); + +#if defined(DEBUG2) || defined(DEBUG3) + htrc("%s\n", g->Message); +#endif + + longjmp(g->jumper[g->jump_level], 1); + } /* endif size OS32 code */ + + /*********************************************************************/ + /* Do the suballocation the simplest way. */ + /*********************************************************************/ + memp = MakePtr(memp, pph->To_Free); /* Points to suballocated block */ + pph->To_Free += size; /* New offset of pool free block */ + pph->FreeBlk -= size; /* New size of pool free block */ +#if defined(DEBUG2) || defined(DEBUG3) + htrc("Done memp=%p used=%d free=%d\n", + memp, pph->To_Free, pph->FreeBlk); +#endif + return (memp); + } /* end of PlugSubAlloc */ + +/***********************************************************************/ +/* This routine makes a pointer from an offset to a memory pointer. */ +/***********************************************************************/ +void *MakePtr(void *memp, OFFSET offset) + { + return ((offset == 0) ? NULL : &((char *)memp)[offset]); + } /* end of MakePtr */ + +/***********************************************************************/ +/* This routine makes an offset from a pointer new format. */ +/***********************************************************************/ +OFFSET MakeOff(void *memp, void *ptr) + { + return ((!ptr) ? 0 : (OFFSET)((char *)ptr - (char *)memp)); + } /* end of MakeOff */ + +/*--------------------- End of PLUGUTIL program -----------------------*/ diff --git a/storage/connect/preparse.h b/storage/connect/preparse.h new file mode 100644 index 00000000000..8b57d487736 --- /dev/null +++ b/storage/connect/preparse.h @@ -0,0 +1,61 @@ +#if !defined(PREPARSE_DEFINED) +#define PREPARSE_DEFINED + +#include "checklvl.h" + +/***********************************************************************/ +/* Struct of variables used by the SQL pre-parsers. */ +/***********************************************************************/ +typedef struct _prepar { + struct _prepar *Next; + char *Debinp; // Start of input buffer + char *Endinp; // End of input buffer + char *Pluginp; // Points on current parsing position + char *Plugbuf; // Start of output buffer + char *Plugptr; // Current output position + char *Debchar; // Next/current start of command + char *Debselp; // Beginning of selection + char *Debline; // Start of current line + char *Plugpar[32]; // Parameters + int Numparms; // Number of defined parameters + int Nprms; // Number of ODBC parameters + int Lines; // Line number + int Chars; // Index of selection start in line + int Endchars; // Index of selection end in line + int Frinp, Frbuf; // 0: no, 1: free, 2: delete Debinp/Plugbuf + int Outsize; // Size of output buffer + FILE *Argfile; // File containing arguments + int Addargs; // 1 if arguments are added to the list + } PREPAR, *PPREP; + +/***********************************************************************/ +/* Struct of variables used by the date format pre-parser. */ +/***********************************************************************/ +typedef struct _datpar { + char *Format; // Points to format to decode + char *Curp; // Points to current parsing position + char *InFmt; // Start of input format + char *OutFmt; // Start of output format + int Index[8]; // Indexes of date values + int Num; // Number of values to retrieve + int Flag; // 1: Input, 2: Output, 4: no output blank + int Outsize; // Size of output buffers + } DATPAR, *PDTP; + +/***********************************************************************/ +/* Preparsers used by SQL language. */ +/***********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +int sqlflex(PPREP pp); +int sqpflex(PPREP pp); +int fmdflex(PDTP pp); + +#ifdef __cplusplus +} +#endif + +#endif // PREPARSE_DEFINED + diff --git a/storage/connect/rcmsg.c b/storage/connect/rcmsg.c new file mode 100644 index 00000000000..e1a71cde9c6 --- /dev/null +++ b/storage/connect/rcmsg.c @@ -0,0 +1,224 @@ +/**************** RCMsg C Program Source Code File (.C) ****************/ +/* PROGRAM NAME: RCMSG */ +/* ------------- */ +/* Version 1.1 */ +/* */ +/* COPYRIGHT */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND: 2005 - 2013 */ +/* */ +/* WHAT THIS PROGRAM DOES */ +/* ----------------------- */ +/* This program simulates LoadString for Unix and Linux. */ +/* */ +/***********************************************************************/ +#include <stdio.h> +#include "resource.h" + +char *GetMsgid(int id) + { + char *p = NULL; + + switch (id) { + case IDS_00: p = "%s"; break; +#if defined(FRENCH) + case IDS_01: p = "%s: erreur d'allocation du buffer de communication de %d octets"; break; + case IDS_02: p = "%s: erreur d'allocation mémoire tampon pour %d colonnes"; break; + case IDS_03: p = "%s: Commande spéciale invalide"; break; + case IDS_04: p = "%s: Wrong number of arguments %d"; break; + case IDS_05: p = "%s"; break; + case IDS_06: p = "%s: Commande dépassant la taille du buffer interne (%d octets)"; break; + case IDS_07: p = "%s: Données (%d octets) tronquées à la taille du buffer"; break; + case IDS_08: p = "%s: Résultat dépassant la taille du buffer interne (%d octets)"; break; + case IDS_09: p = "Erreur dans %s: %s"; break; + case IDS_10: p = "%s: erreur d'allocating mémoire de %d octets"; break; + case IDS_11: p = "%s: mauvaise clé de connexion %d"; break; + case IDS_12: p = "%s: Pas plus de %d connexions autorisées pour un programme"; break; + case IDS_13: p = "%s: clé de connexion invalide %d"; break; + case IDS_14: p = "SafeDB: %s rc=%d"; break; + case IDS_15: p = "Mauvaise Dll de communication appelée par le moteur %s"; break; + case IDS_TAB_01: p = "Qualificateur"; break; + case IDS_TAB_02: p = "Propriétaire"; break; + case IDS_TAB_03: p = "Nom"; break; + case IDS_TAB_04: p = "Type"; break; + case IDS_TAB_05: p = "Remarque"; break; + case IDS_COL_01: p = "Qualif_Table"; break; + case IDS_COL_02: p = "Prop_Tabl"; break; + case IDS_COL_03: p = "Nom_Table"; break; + case IDS_COL_04: p = "Nom_Colonne"; break; + case IDS_COL_05: p = "Type_Données"; break; + case IDS_COL_06: p = "Nom_Type"; break; + case IDS_COL_07: p = "Précision"; break; + case IDS_COL_08: p = "Longueur"; break; + case IDS_COL_09: p = "Echelle"; break; + case IDS_COL_10: p = "Base"; break; + case IDS_COL_11: p = "Nullifiable"; break; + case IDS_COL_12: p = "Remarques"; break; + case IDS_INF_01: p = "Nom_Type"; break; + case IDS_INF_02: p = "Type_Données"; break; + case IDS_INF_03: p = "Précision"; break; + case IDS_INF_04: p = "Préfixe_Litéral"; break; + case IDS_INF_05: p = "Suffixe_Litéral"; break; + case IDS_INF_06: p = "Création_Params"; break; + case IDS_INF_07: p = "Nullifiable"; break; + case IDS_INF_08: p = "Maj_Minuscule"; break; + case IDS_INF_09: p = "Localisable"; break; + case IDS_INF_10: p = "Valeur_Absolue"; break; + case IDS_INF_11: p = "Monnaie"; break; + case IDS_INF_12: p = "Auto_Incrément"; break; + case IDS_INF_13: p = "Nom_Type_Local"; break; + case IDS_INF_14: p = "Echelle_Minimum"; break; + case IDS_INF_15: p = "Echelle_Maximum"; break; + case IDS_PKY_01: p = "Qualif_Table"; break; + case IDS_PKY_02: p = "Prop_Table"; break; + case IDS_PKY_03: p = "Nom_Table"; break; + case IDS_PKY_04: p = "Nom_Colonne"; break; + case IDS_PKY_05: p = "Numéro_Clé"; break; + case IDS_PKY_06: p = "Nom_Clé"; break; + case IDS_FKY_01: p = "PKTable_Qualifier"; break; + case IDS_FKY_02: p = "PKTable_Owner"; break; + case IDS_FKY_03: p = "PKTable_Name"; break; + case IDS_FKY_04: p = "PKColumn_Name"; break; + case IDS_FKY_05: p = "FKTable_Qualifier"; break; + case IDS_FKY_06: p = "FKTable_Owner"; break; + case IDS_FKY_07: p = "FKTable_Name"; break; + case IDS_FKY_08: p = "FKColumn_Name"; break; + case IDS_FKY_09: p = "Key_Seq"; break; + case IDS_FKY_10: p = "Update_Rule"; break; + case IDS_FKY_11: p = "Delete_Rule"; break; + case IDS_FKY_12: p = "FK_Name"; break; + case IDS_FKY_13: p = "PK_Name"; break; + case IDS_STA_01: p = "Table_Qualifier"; break; + case IDS_STA_02: p = "Table_Owner"; break; + case IDS_STA_03: p = "Table_Name"; break; + case IDS_STA_04: p = "Non_Unique"; break; + case IDS_STA_05: p = "Index_Qualifier"; break; + case IDS_STA_06: p = "Index_Name"; break; + case IDS_STA_07: p = "Type"; break; + case IDS_STA_08: p = "Seq_in_Index"; break; + case IDS_STA_09: p = "Column_Name"; break; + case IDS_STA_10: p = "Collation"; break; + case IDS_STA_11: p = "Cardinality"; break; + case IDS_STA_12: p = "Pages"; break; + case IDS_STA_13: p = "Filter_Condition"; break; + case IDS_SPC_01: p = "Champ"; break; + case IDS_SPC_02: p = "Nom_Colonne"; break; + case IDS_SPC_03: p = "Type_Données"; break; + case IDS_SPC_04: p = "Nom_Type"; break; + case IDS_SPC_05: p = "Précision"; break; + case IDS_SPC_06: p = "Longueur"; break; + case IDS_SPC_07: p = "Echelle"; break; + case IDS_SPC_08: p = "Pseudo_Colonne"; break; + case IDS_DRV_01: p = "Description"; break; + case IDS_DRV_02: p = "Attributs"; break; + case IDS_DSC_01: p = "Nom"; break; + case IDS_DSC_02: p = "Description"; break; +#else // English + case IDS_01: p = "%s: error allocating communication buffer of %d bytes"; break; + case IDS_02: p = "%s: error allocating parser memory for %d columns"; break; + case IDS_03: p = "%s: Invalid special command"; break; + case IDS_04: p = "%s: Wrong number of arguments %d"; break; + case IDS_05: p = "%s"; break; + case IDS_06: p = "%s: Command bigger than internal buffer of size = %d"; break; + case IDS_07: p = "%s: Data truncated to buffer size, actual length is %d bytes"; break; + case IDS_08: p = "%s: Result bigger than internal buffer of size = %d"; break; + case IDS_09: p = "Error in %s: %s"; break; + case IDS_10: p = "%s: error allocating instance memory of %d bytes"; break; + case IDS_11: p = "%s: wrong connection key value %d"; break; + case IDS_12: p = "%s: No more than %d connections allowed from one process"; break; + case IDS_13: p = "%s: invalid connection key value %d"; break; + case IDS_14: p = "SafeDB: %s rc=%d"; break; + case IDS_15: p = "Wrong communication Dll called for engine %s"; break; + case IDS_TAB_01: p = "Table_Qualifier"; break; + case IDS_TAB_02: p = "Table_Owner"; break; + case IDS_TAB_03: p = "Table_Name"; break; + case IDS_TAB_04: p = "Table_Type"; break; + case IDS_TAB_05: p = "Remark"; break; + case IDS_COL_01: p = "Table_Qualif"; break; + case IDS_COL_02: p = "Table_Owner"; break; + case IDS_COL_03: p = "Table_Name"; break; + case IDS_COL_04: p = "Column_Name"; break; + case IDS_COL_05: p = "Data_Type"; break; + case IDS_COL_06: p = "Type_Name"; break; + case IDS_COL_07: p = "Precision"; break; + case IDS_COL_08: p = "Length"; break; + case IDS_COL_09: p = "Scale"; break; + case IDS_COL_10: p = "Radix"; break; + case IDS_COL_11: p = "Nullable"; break; + case IDS_COL_12: p = "Remarks"; break; + case IDS_INF_01: p = "Type_Name"; break; + case IDS_INF_02: p = "Data_Type"; break; + case IDS_INF_03: p = "Precision"; break; + case IDS_INF_04: p = "Literal_Prefix"; break; + case IDS_INF_05: p = "Literal_Suffix"; break; + case IDS_INF_06: p = "Create_Params"; break; + case IDS_INF_07: p = "Nullable"; break; + case IDS_INF_08: p = "Case_Sensitive"; break; + case IDS_INF_09: p = "Searchable"; break; + case IDS_INF_10: p = "Unsigned_Attribute"; break; + case IDS_INF_11: p = "Money"; break; + case IDS_INF_12: p = "Auto_Increment"; break; + case IDS_INF_13: p = "Local_Type_Name"; break; + case IDS_INF_14: p = "Minimum_Scale"; break; + case IDS_INF_15: p = "Maximum_Scale"; break; + case IDS_PKY_01: p = "Table_Qualifier"; break; + case IDS_PKY_02: p = "Table_Owner"; break; + case IDS_PKY_03: p = "Table_Name"; break; + case IDS_PKY_04: p = "Column_Name"; break; + case IDS_PKY_05: p = "Key_Seq"; break; + case IDS_PKY_06: p = "Pk_Name"; break; + case IDS_FKY_01: p = "PKTable_Qualifier"; break; + case IDS_FKY_02: p = "PKTable_Owner"; break; + case IDS_FKY_03: p = "PKTable_Name"; break; + case IDS_FKY_04: p = "PKColumn_Name"; break; + case IDS_FKY_05: p = "FKTable_Qualifier"; break; + case IDS_FKY_06: p = "FKTable_Owner"; break; + case IDS_FKY_07: p = "FKTable_Name"; break; + case IDS_FKY_08: p = "FKColumn_Name"; break; + case IDS_FKY_09: p = "Key_Seq"; break; + case IDS_FKY_10: p = "Update_Rule"; break; + case IDS_FKY_11: p = "Delete_Rule"; break; + case IDS_FKY_12: p = "FK_Name"; break; + case IDS_FKY_13: p = "PK_Name"; break; + case IDS_STA_01: p = "Table_Qualifier"; break; + case IDS_STA_02: p = "Table_Owner"; break; + case IDS_STA_03: p = "Table_Name"; break; + case IDS_STA_04: p = "Non_Unique"; break; + case IDS_STA_05: p = "Index_Qualifier"; break; + case IDS_STA_06: p = "Index_Name"; break; + case IDS_STA_07: p = "Type"; break; + case IDS_STA_08: p = "Seq_in_Index"; break; + case IDS_STA_09: p = "Column_Name"; break; + case IDS_STA_10: p = "Collation"; break; + case IDS_STA_11: p = "Cardinality"; break; + case IDS_STA_12: p = "Pages"; break; + case IDS_STA_13: p = "Filter_Condition"; break; + case IDS_SPC_01: p = "Scope"; break; + case IDS_SPC_02: p = "Column_Name"; break; + case IDS_SPC_03: p = "Data_Type"; break; + case IDS_SPC_04: p = "Type_Name"; break; + case IDS_SPC_05: p = "Precision"; break; + case IDS_SPC_06: p = "Length"; break; + case IDS_SPC_07: p = "Scale"; break; + case IDS_SPC_08: p = "Pseudo_Column"; break; + case IDS_DRV_01: p = "Description"; break; + case IDS_DRV_02: p = "Attributes"; break; + case IDS_DSC_01: p = "Name"; break; + case IDS_DSC_02: p = "Description"; break; +#endif // English + } // endswitch(id) + + return p; + } // end of GetMsgid + +int GetRcString(int id, char *buf, int bufsize) + { + char *p = NULL, msg[32]; + + if (!(p = GetMsgid(id))) { + sprintf(msg, "ID=%d unknown", id); + p = msg; + } // endif p + + return sprintf(buf, "%.*s", bufsize-1, p); + } // end of GetRcString diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp new file mode 100644 index 00000000000..77f2f43ec31 --- /dev/null +++ b/storage/connect/reldef.cpp @@ -0,0 +1,427 @@ +/************* RelDef CPP Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: REFDEF */ +/* ------------- */ +/* Version 1.3 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the DB definition related routines. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <sqlext.h> +#else +#include <dlfcn.h> // dlopen(), dlclose(), dlsym() ... +#include "osutil.h" +//#include "sqlext.h" +#endif + +/***********************************************************************/ +/* Include application header files */ +/* */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing DB application declarations. */ +/* catalog.h is header containing DB description declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "mycat.h" +#include "reldef.h" +#include "colblk.h" +#include "filamap.h" +#include "filamfix.h" +#include "filamvct.h" +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT +#include "tabdos.h" +#include "valblk.h" +#include "tabmul.h" + +/***********************************************************************/ +/* External static variables. */ +/***********************************************************************/ +//extern "C" char plgini[]; + +/* --------------------------- Class RELDEF -------------------------- */ + +/***********************************************************************/ +/* RELDEF Constructor. */ +/***********************************************************************/ +RELDEF::RELDEF(void) + { + Next = NULL; + To_Cols = NULL; + Name = NULL; + Database = NULL; + Cat = NULL; + } // end of RELDEF constructor + +/* --------------------------- Class TABDEF -------------------------- */ + +/***********************************************************************/ +/* TABDEF Constructor. */ +/***********************************************************************/ +TABDEF::TABDEF(void) + { + Owner = NULL; + Desc = NULL; + Catfunc = FNC_NO; + Card = 0; + Elemt = 0; + Sort = 0; + Multiple = 0; + Degree = 0; + Pseudo = 0; + Read_Only = false; + } // end of TABDEF constructor + +/***********************************************************************/ +/* Define: initialize the table definition block from XDB file. */ +/***********************************************************************/ +bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) + { +//char buf[8]; + int poff = 0; + void *memp = cat->Descp; + + Name = (PSZ)PlugSubAlloc(g, memp, strlen(name) + 1); + strcpy(Name, name); + Cat = cat; + Catfunc = GetFuncID(Cat->GetStringCatInfo(g, "Catfunc", NULL)); + Elemt = cat->GetIntCatInfo("Elements", 0); + Multiple = cat->GetIntCatInfo("Multiple", 0); + Degree = cat->GetIntCatInfo("Degree", 0); + Read_Only = cat->GetBoolCatInfo("ReadOnly", false); + const char *data_charset_name= cat->GetStringCatInfo(g, "Data_charset", NULL); + m_data_charset= data_charset_name ? + get_charset_by_csname(data_charset_name, MY_CS_PRIMARY, 0): + NULL; + + // Get The column definitions + if ((poff = cat->GetColCatInfo(g, this)) < 0) + return true; + + // Do the definition of AM specific fields + return DefineAM(g, am, poff); + } // end of Define + +/* --------------------------- Class OEMDEF -------------------------- */ + +/***********************************************************************/ +/* GetXdef: get the external TABDEF from OEM module. */ +/***********************************************************************/ +PTABDEF OEMDEF::GetXdef(PGLOBAL g) + { + typedef PTABDEF (__stdcall *XGETDEF) (PGLOBAL, void *); + char c, getname[40] = "Get"; + PTABDEF xdefp; + XGETDEF getdef = NULL; + PCATLG cat = Cat; + void *memp = cat->Descp; + +#if defined(WIN32) + // Is the DLL already loaded? + if (!Hdll && !(Hdll = GetModuleHandle(Module))) + // No, load the Dll implementing the function + if (!(Hdll = LoadLibrary(Module))) { + char buf[256]; + DWORD rc = GetLastError(); + + sprintf(g->Message, MSG(DLL_LOAD_ERROR), rc, Module); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + strcat(strcat(g->Message, ": "), buf); + return NULL; + } // endif hDll + + // The exported name is always in uppercase + for (int i = 0; ; i++) { + c = Subtype[i]; + getname[i + 3] = toupper(c); + if (!c) break; + } // endfor i + + // Get the function returning an instance of the external DEF class + if (!(getdef = (XGETDEF)GetProcAddress((HINSTANCE)Hdll, getname))) { + sprintf(g->Message, MSG(PROCADD_ERROR), GetLastError(), getname); + FreeLibrary((HMODULE)Hdll); + return NULL; + } // endif getdef +#else // !WIN32 + const char *error = NULL; + // Is the library already loaded? +// if (!Hdll && !(Hdll = ???)) + // Load the desired shared library + if (!(Hdll = dlopen(Module, RTLD_LAZY))) { + error = dlerror(); + sprintf(g->Message, MSG(SHARED_LIB_ERR), Module, SVP(error)); + return NULL; + } // endif Hdll + + // The exported name is always in uppercase + for (int i = 0; ; i++) { + c = Subtype[i]; + getname[i + 3] = toupper(c); + if (!c) break; + } // endfor i + + // Get the function returning an instance of the external DEF class + if (!(getdef = (XGETDEF)dlsym(Hdll, getname))) { + error = dlerror(); + sprintf(g->Message, MSG(GET_FUNC_ERR), getname, SVP(error)); + dlclose(Hdll); + return NULL; + } // endif getdef +#endif // !WIN32 + + // Just in case the external Get function does not set error messages + sprintf(g->Message, MSG(DEF_ALLOC_ERROR), Subtype); + + // Get the table definition block + if (!(xdefp = getdef(g, memp))) + return NULL; + + // Have the external class do its complete definition + if (!cat->Cbuf) { + // Suballocate a temporary buffer for the entire column section + cat->Cblen = cat->GetSizeCatInfo("Colsize", "8K"); + cat->Cbuf = (char*)PlugSubAlloc(g, NULL, cat->Cblen); + } // endif Cbuf + + // Here "OEM" should be replace by a more useful value + if (xdefp->Define(g, cat, Name, "OEM")) + return NULL; + + // Ok, return external block + return xdefp; + } // end of GetXdef + +/***********************************************************************/ +/* DeleteTableFile: Delete an OEM table file if applicable. */ +/***********************************************************************/ +bool OEMDEF::DeleteTableFile(PGLOBAL g) + { + if (!Pxdef) + Pxdef = GetXdef(g); + + return (Pxdef) ? Pxdef->DeleteTableFile(g) : true; + } // end of DeleteTableFile + +/***********************************************************************/ +/* Define: initialize the table definition block from XDB file. */ +/***********************************************************************/ +bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + void *memp = Cat->Descp; + + Module = Cat->GetStringCatInfo(g, "Module", ""); + Subtype = Cat->GetStringCatInfo(g, "Subtype", Module); + + if (!*Module) + Module = Subtype; + + Desc = (char*)PlugSubAlloc(g, memp, strlen(Module) + + strlen(Subtype) + 3); + sprintf(Desc, "%s(%s)", Module, Subtype); + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode) + { + RECFM rfm; + PTDBASE tdbp = NULL; + + // If define block not here yet, get it now + if (!Pxdef && !(Pxdef = GetXdef(g))) + return NULL; // Error + + /*********************************************************************/ + /* Allocate a TDB of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + if (!(tdbp = (PTDBASE)Pxdef->GetTable(g, mode))) + return NULL; + else + rfm = tdbp->GetFtype(); + + if (rfm == RECFM_NAF) + return tdbp; + else if (rfm == RECFM_OEM) { + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); // No block optimization yet + + return tdbp; + } // endif OEM + + /*********************************************************************/ + /* The OEM table is based on a file type (currently DOS+ only) */ + /*********************************************************************/ + assert (rfm == RECFM_VAR || rfm == RECFM_FIX || + rfm == RECFM_BIN || rfm == RECFM_VCT); + + PTXF txfp = NULL; + PDOSDEF defp = (PDOSDEF)Pxdef; + bool map = defp->Mapped && mode != MODE_INSERT && + !(PlgGetUser(g)->UseTemp == TMP_FORCE && + (mode == MODE_UPDATE || mode == MODE_DELETE)); + int cmpr = defp->Compressed; + + /*********************************************************************/ + /* Allocate table and file processing class of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + if (!((PTDBDOS)tdbp)->GetTxfp()) { + if (cmpr) { +#if defined(ZIP_SUPPORT) + if (cmpr == 1) + txfp = new(g) ZIPFAM(defp); + else { + strcpy(g->Message, "Compress 2 not supported yet"); +// txfp = new(g) ZLBFAM(defp); + return NULL; + } // endelse +#else // !ZIP_SUPPORT + strcpy(g->Message, "Compress not supported"); + return NULL; +#endif // !ZIP_SUPPORT + } else if (rfm == RECFM_VAR) { + if (map) + txfp = new(g) MAPFAM(defp); + else + txfp = new(g) DOSFAM(defp); + + } else if (rfm == RECFM_FIX || rfm == RECFM_FIX) { + if (map) + txfp = new(g) MPXFAM(defp); + else + txfp = new(g) FIXFAM(defp); + + } else if (rfm == RECFM_VCT) { + assert (Pxdef->GetDefType() == TYPE_AM_VCT); + + if (map) + txfp = new(g) VCMFAM((PVCTDEF)defp); + else + txfp = new(g) VCTFAM((PVCTDEF)defp); + + } // endif's + + ((PTDBDOS)tdbp)->SetTxfp(txfp); + } // endif Txfp + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); + + return tdbp; + } // end of GetTable + +/* --------------------------- Class COLCRT -------------------------- */ + +/***********************************************************************/ +/* COLCRT Constructors. */ +/***********************************************************************/ +COLCRT::COLCRT(PSZ name) + { + Next = NULL; + Name = name; + Desc = NULL; + Decode = NULL; + Fmt = NULL; + Offset = -1; + Long = -1; +//Freq = -1; + Key = -1; + Prec = -1; + Opt = -1; + DataType = '*'; + } // end of COLCRT constructor for table creation + +COLCRT::COLCRT(void) + { + Next = NULL; + Name = NULL; + Desc = NULL; + Decode = NULL; + Fmt = NULL; + Offset = 0; + Long = 0; +//Freq = 0; + Key = 0; + Prec = 0; + Opt = 0; + DataType = '*'; + } // end of COLCRT constructor for table & view definition + +/* --------------------------- Class COLDEF -------------------------- */ + +/***********************************************************************/ +/* COLDEF Constructor. */ +/***********************************************************************/ +COLDEF::COLDEF(void) : COLCRT() + { + Buf_Type = TYPE_ERROR; + Clen = 0; + Poff = 0; + memset(&F, 0, sizeof(FORMAT)); + Flags = 0; + } // end of COLDEF constructor + +/***********************************************************************/ +/* Define: initialize a column definition from a COLINFO structure. */ +/***********************************************************************/ +int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff) + { + Name = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Name) + 1); + strcpy(Name, cfp->Name); + + Poff = poff; + Buf_Type = cfp->Type; + + if ((Clen = GetTypeSize(Buf_Type, cfp->Length)) <= 0) { + sprintf(g->Message, MSG(BAD_COL_TYPE), GetTypeName(Buf_Type), Name); + return -1; + } // endswitch + + strcpy(F.Type, GetFormatType(Buf_Type)); + F.Length = cfp->Length; + F.Prec = cfp->Prec; + Offset = (cfp->Offset < 0) ? poff : cfp->Offset; + Long = cfp->Length; + Opt = cfp->Opt; + Key = cfp->Key; +//Freq = cfp->Freq; + + if (cfp->Remark && *cfp->Remark) { + Desc = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Remark) + 1); + strcpy(Desc, cfp->Remark); + } // endif Remark + + if (cfp->Datefmt) { + Decode = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Datefmt) + 1); + strcpy(Decode, cfp->Datefmt); + } // endif Datefmt + + if (cfp->Fieldfmt) { + Fmt = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Fieldfmt) + 1); + strcpy(Fmt, cfp->Fieldfmt); + } // endif Fieldfmt + + Flags = cfp->Flags; + return (Flags & U_VIRTUAL) ? 0 : Long; + } // end of Define + +/* ------------------------- End of RelDef --------------------------- */ diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h new file mode 100644 index 00000000000..15cdb772294 --- /dev/null +++ b/storage/connect/reldef.h @@ -0,0 +1,198 @@ +/*************** RelDef H Declares Source Code File (.H) ***************/ +/* Name: RELDEF.H Version 1.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2012 */ +/* */ +/* This file contains the DEF classes definitions. */ +/***********************************************************************/ + +#ifndef __RELDEF_H +#define __RELDEF_H + +#include "block.h" +#include "catalog.h" +#include "my_sys.h" + +typedef class INDEXDEF *PIXDEF; + +/***********************************************************************/ +/* Table or View (relation) definition block. */ +/***********************************************************************/ +class DllExport RELDEF : public BLOCK { // Relation definition block + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + RELDEF(void); // Constructor + + // Implementation + PRELDEF GetNext(void) {return Next;} + PSZ GetName(void) {return Name;} + PSZ GetDB(void) {return (PSZ)Database;} + PCOLDEF GetCols(void) {return To_Cols;} + void SetCols(PCOLDEF pcd) {To_Cols = pcd;} + PCATLG GetCat(void) {return Cat;} + virtual const char *GetType(void) = 0; + virtual AMT GetDefType(void) = 0; + + // Methods + virtual bool DeleteTableFile(PGLOBAL g) {return true;} + virtual bool Indexable(void) {return false;} + virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) = 0; + virtual PTDB GetTable(PGLOBAL g, MODE mode) = 0; + + protected: + PRELDEF Next; /* To next definition block */ + PSZ Name; /* Name of the view */ + LPCSTR Database; /* Table database */ + PCOLDEF To_Cols; /* To a list of column desc */ + PCATLG Cat; /* To DB catalog info */ + }; // end of RELDEF + +/***********************************************************************/ +/* These classes correspond to the data base description contained in */ +/* a .XDB file the A.M. DOS, FIX, CSV, MAP, BIN, VCT, PLG, ODBC, DOM. */ +/***********************************************************************/ +class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + // Constructor + TABDEF(void); // Constructor + + // Implementation + int GetDegree(void) {return Degree;} + void SetDegree(int d) {Degree = d;} + int GetElemt(void) {return Elemt;} + void SetNext(PTABDEF tdfp) {Next = tdfp;} + int GetMultiple(void) {return Multiple;} + int GetPseudo(void) {return Pseudo;} + PSZ GetPath(void) + {return (Database) ? (PSZ)Database : Cat->GetDataPath();} + bool SepIndex(void) {return Cat->GetBoolCatInfo("SepIndex", false);} + bool IsReadOnly(void) {return Read_Only;} + virtual AMT GetDefType(void) {return TYPE_AM_TAB;} + virtual PIXDEF GetIndx(void) {return NULL;} + virtual void SetIndx(PIXDEF xp) {} + virtual bool IsHuge(void) {return false;} + const CHARSET_INFO *data_charset() {return m_data_charset;} + + // Methods + bool DropTable(PGLOBAL g, PSZ name); + virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am); + virtual bool DefineAM(PGLOBAL, LPCSTR, int) = 0; + + protected: + // Members + PSZ Owner; /* Table owner (for ODBC) */ + PSZ Desc; /* Table description */ + uint Catfunc; /* Catalog function ID */ + int Card; /* (max) number of rows in table */ + int Elemt; /* Number of rows in blocks or rowset */ + int Sort; /* Table already sorted ??? */ + int Multiple; /* 0: No 1: DIR 2: Section 3: filelist */ + int Degree; /* Number of columns in the table */ + int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */ + bool Read_Only; /* true for read only tables */ + const CHARSET_INFO *m_data_charset; + }; // end of TABDEF + +/***********************************************************************/ +/* Externally defined OEM tables. */ +/***********************************************************************/ +class DllExport OEMDEF : public TABDEF { /* OEM table */ + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + public: + // Constructor + OEMDEF(void) {Hdll = NULL; Pxdef = NULL; Module = Subtype = NULL;} + + // Implementation + virtual const char *GetType(void) {return "OEM";} + virtual AMT GetDefType(void) {return TYPE_AM_OEM;} + + // Methods + virtual bool DeleteTableFile(PGLOBAL g); + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE mode); + + protected: + PTABDEF GetXdef(PGLOBAL g); + + // Members +#if defined(WIN32) + HANDLE Hdll; /* Handle to the external DLL */ +#else // !WIN32 + void *Hdll; /* Handle for the loaded shared library */ +#endif // !WIN32 + PTABDEF Pxdef; /* Pointer to the external TABDEF class */ + char *Module; /* Path/Name of the DLL implenting it */ + char *Subtype; /* The name of the OEM table sub type */ + }; // end of OEMDEF + +/***********************************************************************/ +/* Column definition block used during creation. */ +/***********************************************************************/ +class DllExport COLCRT : public BLOCK { /* Column description block */ + friend class TABDEF; + public: + COLCRT(PSZ name); // Constructor + COLCRT(void); // Constructor (for views) + + // Implementation + PSZ GetName(void) {return Name;} + PSZ GetDecode(void) {return Decode;} + PSZ GetFmt(void) {return Fmt;} + int GetOpt(void) {return Opt;} + int GetLong(void) {return Long;} + int GetOffset(void) {return Offset;} + void SetOffset(int offset) {Offset = offset;} + + protected: + PCOLCRT Next; /* To next block */ + PSZ Name; /* Column name */ + PSZ Desc; /* Column description */ + PSZ Decode; /* Date format */ + PSZ Fmt; /* Input format for formatted files */ + int Offset; /* Offset of field within record */ + int Long; /* Length of field in file record (!BIN) */ + int Key; /* Key (greater than 1 if multiple) */ + int Prec; /* Precision for float values */ + int Opt; /* 0:Not 1:clustered 2:sorted-asc 3:desc */ + char DataType; /* Internal data type (C, N, F, T) */ + }; // end of COLCRT + +/***********************************************************************/ +/* Column definition block. */ +/***********************************************************************/ +class DllExport COLDEF : public COLCRT { /* Column description block */ + friend class CATALOG; + friend class PLUGCAT; + friend class MYCAT; + friend class COLBLK; + friend class DBFFAM; + public: + COLDEF(void); // Constructor + + // Implementation + PCOLDEF GetNext(void) {return (PCOLDEF)Next;} + void SetNext(PCOLDEF pcdf) {Next = pcdf;} + int GetLength(void) {return (int)F.Length;} + int GetClen(void) {return Clen;} + int GetType(void) {return Buf_Type;} + int GetPoff(void) {return Poff;} + int Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff); + void Define(PGLOBAL g, PCOL colp); + + protected: + int Buf_Type; /* Internal data type */ + int Clen; /* Internal data size in chars (bytes) */ + int Poff; /* Calculated offset for Packed tables */ + FORMAT F; /* Output format (should be in COLCRT) */ + ushort Flags; /* Used by MariaDB CONNECT handler */ + }; // end of COLDEF + +#endif // __RELDEF_H + diff --git a/storage/connect/resource.h b/storage/connect/resource.h new file mode 100644 index 00000000000..773942cf280 --- /dev/null +++ b/storage/connect/resource.h @@ -0,0 +1,139 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by PlgSock.rc +// +#define IDS_00 115 +#define IDS_01 116 +#define IDS_02 117 +#define IDS_03 118 +#define IDS_04 119 +#define IDS_05 120 +#define IDS_06 121 +#define IDS_07 122 +#define IDS_08 123 +#define IDS_09 124 +#define IDS_10 125 +#define IDS_11 126 +#define IDS_12 127 +#define IDS_13 128 +#define IDS_14 129 +#define IDS_15 130 +#define IDS_16 131 +#define IDS_17 132 +#define IDS_18 133 +#define IDS_19 134 +#define IDS_20 135 +#define IDS_21 136 +#define IDS_TABLES 143 +#define IDS_TAB_01 144 +#define IDS_TAB_02 145 +#define IDS_TAB_03 146 +#define IDS_TAB_04 147 +#define IDS_TAB_05 148 +#define IDS_COLUMNS 159 +#define IDS_COL_01 160 +#define IDS_COL_02 161 +#define IDS_COL_03 162 +#define IDS_COL_04 163 +#define IDS_COL_05 164 +#define IDS_COL_06 165 +#define IDS_COL_07 166 +#define IDS_COL_08 167 +#define IDS_COL_09 168 +#define IDS_COL_10 169 +#define IDS_COL_11 170 +#define IDS_COL_12 171 +#define IDS_INFO 175 +#define IDS_INF_01 176 +#define IDS_INF_02 177 +#define IDS_INF_03 178 +#define IDS_INF_04 179 +#define IDS_INF_05 180 +#define IDS_INF_06 181 +#define IDS_INF_07 182 +#define IDS_INF_08 183 +#define IDS_INF_09 184 +#define IDS_INF_10 185 +#define IDS_INF_11 186 +#define IDS_INF_12 187 +#define IDS_INF_13 188 +#define IDS_INF_14 189 +#define IDS_INF_15 190 +#define IDS_PKEY 191 +#define IDS_PKY_01 192 +#define IDS_PKY_02 193 +#define IDS_PKY_03 194 +#define IDS_PKY_04 195 +#define IDS_PKY_05 196 +#define IDS_PKY_06 197 +#define IDS_FKEY 207 +#define IDS_FKY_01 208 +#define IDS_FKY_02 209 +#define IDS_FKY_03 210 +#define IDS_FKY_04 211 +#define IDS_FKY_05 212 +#define IDS_FKY_06 213 +#define IDS_FKY_07 214 +#define IDS_FKY_08 215 +#define IDS_FKY_09 216 +#define IDS_FKY_10 217 +#define IDS_FKY_11 218 +#define IDS_FKY_12 219 +#define IDS_FKY_13 220 +#define IDS_STAT 223 +#define IDS_STA_01 224 +#define IDS_STA_02 225 +#define IDS_STA_03 226 +#define IDS_STA_04 227 +#define IDS_STA_05 228 +#define IDS_STA_06 229 +#define IDS_STA_07 230 +#define IDS_STA_08 231 +#define IDS_STA_09 232 +#define IDS_STA_10 233 +#define IDS_STA_11 234 +#define IDS_STA_12 235 +#define IDS_STA_13 236 +#define IDS_SPCOLS 1247 +#define IDS_SPC_01 1248 +#define IDS_SPC_02 1249 +#define IDS_SPC_03 1250 +#define IDS_SPC_04 1251 +#define IDS_SPC_05 1252 +#define IDS_SPC_06 1253 +#define IDS_SPC_07 1254 +#define IDS_SPC_08 1255 +#define IDS_CNX 1263 +#define IDS_CNX_01 1264 +#define IDS_CNX_02 1265 +#define IDS_CNX_03 1266 +#define IDS_CNX_04 1267 +#define IDS_PLGCOL 1279 +#define IDS_PLG_01 1280 +#define IDS_PLG_02 1281 +#define IDS_PLG_03 1282 +#define IDS_PLG_04 1283 +#define IDS_PLG_05 1284 +#define IDS_PLG_06 1285 +#define IDS_PLG_07 1286 +#define IDS_PLG_08 1287 +#define IDS_PLG_09 1288 +#define IDS_DRIVER 1290 +#define IDS_DRV_01 1291 +#define IDS_DRV_02 1292 +#define IDS_DSRC 1295 +#define IDS_DSC_01 1296 +#define IDS_DSC_02 1297 +//#define IDS_DSC_03 1298 +//#define IDS_DSC_04 1299 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 1300 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1440 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/storage/connect/tabcol.cpp b/storage/connect/tabcol.cpp new file mode 100644 index 00000000000..f0e010291c3 --- /dev/null +++ b/storage/connect/tabcol.cpp @@ -0,0 +1,170 @@ +/************* TabCol C++ Functions Source Code File (.CPP) ************/ +/* Name: TABCOL.CPP Version 2.6 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* This file contains the PlugDB++ XTAB, COLUMN and XORDER methods. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/* tabcol.h is header containing XTAB, and XORDER declares. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "tabcol.h" + +/***********************************************************************/ +/* XTAB public constructor (in which Correl defaults to Name). */ +/***********************************************************************/ +XTAB::XTAB(LPCSTR name, LPCSTR correl) : Name(name) + { + Next = NULL; + To_Tdb = NULL; + Correl = (correl) ? correl : name; + Creator = NULL; + Qualifier = NULL; + +#ifdef DEBTRACE + htrc(" making new TABLE %s %s\n", Name, Correl); +#endif + } // end of XTAB constructor + +/***********************************************************************/ +/* XTAB public constructor as a copy of another table. */ +/***********************************************************************/ +XTAB::XTAB(PTABLE tp) : Name(tp->Name) + { + Next = NULL; + To_Tdb = NULL; + Correl = tp->Correl; + Creator = tp->Creator; + Qualifier = tp->Qualifier; + +#ifdef DEBTRACE + htrc(" making copy TABLE %s %s\n", Name, Correl); +#endif + } // end of XTAB constructor + +/***********************************************************************/ +/* Link the tab2 tables to the tab1(this) table chain. */ +/***********************************************************************/ +PTABLE XTAB::Link(PTABLE tab2) + { + PTABLE tabp; + +#ifdef DEBTRACE + htrc("Linking tables %s... to %s\n", Name, tab2->Name); +#endif + + for (tabp = this; tabp->Next; tabp = tabp->Next) ; + + tabp->Next = tab2; + return (this); + } /* end of Link */ + +/***********************************************************************/ +/* Make file output of XTAB contents. */ +/***********************************************************************/ +void XTAB::Print(PGLOBAL g, FILE *f, uint n) + { + char m[64]; + + memset(m, ' ', n); /* Make margin string */ + m[n] = '\0'; + + for (PTABLE tp = this; tp; tp = tp->Next) { + fprintf(f, "%sTABLE: %s.%s %s\n", + m, SVP(tp->Creator), tp->Name, SVP(tp->Correl)); + PlugPutOut(g, f, TYPE_TDB, tp->To_Tdb, n + 2); + } /* endfor tp */ + + } /* end of Print */ + +/***********************************************************************/ +/* Make string output of XTAB contents. */ +/***********************************************************************/ +void XTAB::Print(PGLOBAL g, char *ps, uint z) + { + char buf[128]; + int i, n = (int)z - 1; + + *ps = '\0'; + + for (PTABLE tp = this; tp && n > 0; tp = tp->Next) { + i = sprintf(buf, "TABLE: %s.%s %s To_Tdb=%p ", + SVP(tp->Creator), tp->Name, SVP(tp->Correl), tp->To_Tdb); + strncat(ps, buf, n); + n -= i; + } // endif tp + + } /* end of Print */ + + +/***********************************************************************/ +/* COLUMN public constructor. */ +/***********************************************************************/ +COLUMN::COLUMN(LPCSTR name) : Name(name) + { + To_Table = NULL; + To_Col = NULL; + Qualifier = NULL; + +#ifdef DEBTRACE + htrc(" making new COLUMN %s\n", Name); +#endif + } // end of COLUMN constructor + +/***********************************************************************/ +/* COLUMN SetFormat: should never be called. */ +/***********************************************************************/ +bool COLUMN::SetFormat(PGLOBAL g, FORMAT& fmt) + { + strcpy(g->Message, MSG(NO_FORMAT_COL)); + return true; + } // end of SetFormat + +/***********************************************************************/ +/* Make file output of COLUMN contents. */ +/***********************************************************************/ +void COLUMN::Print(PGLOBAL g, FILE *f, uint n) + { + char m[64]; + + memset(m, ' ', n); // Make margin string + m[n] = '\0'; + + if (Name) + fprintf(f, "%sCOLUMN: %s.%s\n", m, + ((!Qualifier) ? (PSZ)"?" : Qualifier), Name); + else // LNA + fprintf(f, "%sC%d\n", m, (!Qualifier) ? 0 : *(int *)Qualifier); + + PlugPutOut(g, f, TYPE_TABLE, To_Table, n + 2); + PlugPutOut(g, f, TYPE_XOBJECT, To_Col, n + 2); + } /* end of Print */ + +/***********************************************************************/ +/* Make string output of COLUMN contents. */ +/***********************************************************************/ +void COLUMN::Print(PGLOBAL g, char *ps, uint z) + { + char buf[80]; + + if (Name) + sprintf(buf, "COLUMN: %s.%s table=%p col=%p", + ((!Qualifier) ? (PSZ)"?" : Qualifier), Name, To_Table, To_Col); + else // LNA + sprintf(buf, "C%d", (!Qualifier) ? 0 : *(int *)Qualifier); + + strncpy(ps, buf, z); + ps[z - 1] = '\0'; + } /* end of Print */ diff --git a/storage/connect/tabcol.h b/storage/connect/tabcol.h new file mode 100644 index 00000000000..5cc2050f872 --- /dev/null +++ b/storage/connect/tabcol.h @@ -0,0 +1,110 @@ +/*************** TabCol H Declares Source Code File (.H) ***************/ +/* Name: TABCOL.H Version 2.7 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* This file contains the XTAB, COLUMN and XORDER class definitions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include required application header files */ +/* block.h is header containing Block global declarations. */ +/***********************************************************************/ +#include "xobject.h" + +/***********************************************************************/ +/* Definition of class XTAB with all its method functions. */ +/***********************************************************************/ + class DllExport XTAB: public BLOCK { // Table Name-Owner-Correl block. + public: + // Constructors + XTAB(LPCSTR name, LPCSTR correl = NULL); + XTAB(PTABLE tp); + + // Implementation + PTABLE GetNext(void) {return Next;} + PTDB GetTo_Tdb(void) {return To_Tdb;} + LPCSTR GetName(void) {return Name;} + LPCSTR GetCorrel(void) {return Correl;} + LPCSTR GetCreator(void) {return Creator;} + LPCSTR GetQualifier(void) {return Qualifier;} + void SetTo_Tdb(PTDB tdbp) {To_Tdb = tdbp;} + void SetName(LPCSTR name) {Name = name;} + void SetCorrel(LPCSTR correl) {Correl = correl;} + void SetCreator(LPCSTR crname) {Creator = crname;} + void SetQualifier(LPCSTR qname) {Qualifier = qname;} + + // Methods + PTABLE Link(PTABLE); + void Print(PGLOBAL g, FILE *f, uint n); + void Print(PGLOBAL g, char *ps, uint z); + + protected: + // Members + PTABLE Next; // Points to next table in chain + PTDB To_Tdb; // Points to Table description Block + LPCSTR Name; // Table name (can be changed by LNA and PLG) + LPCSTR Correl; // Correlation name + LPCSTR Creator; // Creator name + LPCSTR Qualifier; // Qualifier name + }; // end of class XTAB + + +/***********************************************************************/ +/* Definition of class COLUMN with all its method functions. */ +/* Note: because of LNA routines, the constantness of Name was */ +/* removed and constructing a COLUMN with null name was allowed. */ +/* Perhaps this should be replaced by the use of a specific class. */ +/***********************************************************************/ +class DllExport COLUMN: public XOBJECT { // Column Name/Qualifier block. + public: + // Constructor + COLUMN(LPCSTR name); + + // Implementation + virtual int GetType(void) {return TYPE_COLUMN;} + virtual int GetResultType(void) {assert(false); return TYPE_VOID;} + virtual int GetLength(void) {assert(false); return 0;} + virtual int GetLengthEx(void) {assert(false); return 0;} + virtual int GetPrecision() {assert(false); return 0;}; + LPCSTR GetName(void) {return Name;} + LPCSTR GetQualifier(void) {return Qualifier;} + PTABLE GetTo_Table(void) {return To_Table;} + PCOL GetTo_Col(void) {return To_Col;} + void SetQualifier(LPCSTR qualif) {Qualifier = qualif;} + void SetTo_Table(PTABLE tablep) {To_Table = tablep;} + void SetTo_Col(PCOL colp) {To_Col = colp;} + + // Methods + virtual void Print(PGLOBAL g, FILE *f, uint n); + virtual void Print(PGLOBAL g, char *ps, uint z); + // All methods below should never be used for COLUMN's + virtual void Reset(void) {assert(false);} + virtual bool Compare(PXOB) {assert(false); return false;} + virtual bool SetFormat(PGLOBAL, FORMAT&); + virtual bool Eval(PGLOBAL) {assert(false); return true;} + virtual int CheckSpcCol(PTDB, int) {assert(false); return 2;} + virtual bool CheckSort(PTDB) {assert(false); return false;} + virtual void MarkCol(ushort) {assert(false);} + + private: + // Members + PTABLE To_Table; // Point to Table Name Block + PCOL To_Col; // Points to Column Description Block + LPCSTR const Name; // Column name + LPCSTR Qualifier; // Qualifier name + }; // end of class COLUMN + +/***********************************************************************/ +/* Definition of class SPCCOL with all its method functions. */ +/* Note: Currently the special columns are ROWID, ROWNUM, FILEID, */ +/* SERVID, TABID, and CONID. */ +/***********************************************************************/ +class SPCCOL: public COLUMN { // Special Column Name/Qualifier block. + public: + // Constructor + SPCCOL(LPCSTR name) : COLUMN(name) {} + + private: + // Members + }; // end of class SPCCOL diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp new file mode 100644 index 00000000000..288396276bc --- /dev/null +++ b/storage/connect/tabdos.cpp @@ -0,0 +1,1269 @@ +/************* TabDos C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABDOS */ +/* ------------- */ +/* Version 4.8 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the DOS tables classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <sys\timeb.h> // For testing only +#include <fcntl.h> +#include <errno.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* filamtxt.h is header containing the file AM classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "osutil.h" +#include "plgdbsem.h" +#include "catalog.h" +#include "mycat.h" +#include "xindex.h" +#include "filamap.h" +#include "filamfix.h" +#include "filamdbf.h" +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT +#include "tabdos.h" +#include "tabfix.h" +#include "tabmul.h" + +#define PLGINI "plugdb.ini" // Configuration settings file + +#if defined(UNIX) +#define _fileno fileno +#define _O_RDONLY O_RDONLY +#endif + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +int num_read, num_there, num_eq[2]; // Statistics +extern "C" char plgini[_MAX_PATH]; +extern "C" int trace; + +/***********************************************************************/ +/* Min and Max blocks contains zero ended fields (blank = false). */ +/* No conversion of block values (check = true). */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len = 0, int prec = 0, + bool check = true, bool blank = false); + +/* --------------------------- Class DOSDEF -------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +DOSDEF::DOSDEF(void) + { + Pseudo = 3; + Fn = NULL; + Ofn = NULL; + To_Indx = NULL; + Recfm = RECFM_VAR; + Mapped = false; + Padded = false; + Huge = false; + Accept = false; + Eof = false; + To_Pos = NULL; + Compressed = 0; + Lrecl = 0; + AvgLen = 0; + Block = 0; + Last = 0; + Blksize = 0; + Maxerr = 0; + ReadMode = 0; + Ending = 0; +//Mtime = 0; + } // end of DOSDEF constructor + +/***********************************************************************/ +/* DeleteTableFile: Delete DOS/UNIX table files using platform API. */ +/* If the table file is protected (declared as read/only) we still */ +/* erase the the eventual optimize and index files but return true. */ +/***********************************************************************/ +bool DOSDEF::DeleteTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc = false; + + // Now delete the table file itself if not protected + if (!IsReadOnly()) { + rc = Erase(filename); + } else + rc =true; + + return rc; // Return true if error + } // end of DeleteTableFile + +/***********************************************************************/ +/* Erase: This was made a separate routine because a strange thing */ +/* happened when DeleteTablefile was defined for the VCTDEF class: */ +/* when called from Catalog, the DOSDEF routine was still called even */ +/* when the class was VCTDEF. It also minimizes the specific code. */ +/***********************************************************************/ +bool DOSDEF::Erase(char *filename) + { + bool rc; + + PlugSetPath(filename, Fn, GetPath()); +#if defined(WIN32) + rc = !DeleteFile(filename); +#else // UNIX + rc = remove(filename); +#endif // UNIX + + return rc; // Return true if error + } // end of Erase + +/***********************************************************************/ +/* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */ +/***********************************************************************/ +bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf) + { + char *ftype; + char filename[_MAX_PATH]; + bool sep, rc = false; + + if (!To_Indx) + return false; // No index + + // If true indexes are in separate files + sep = Cat->GetBoolCatInfo("SepIndex", false); + + if (!sep && pxdf) { + strcpy(g->Message, MSG(NO_RECOV_SPACE)); + return true; + } // endif sep + + switch (Recfm) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm); + return true; + } // endswitch Ftype + + /*********************************************************************/ + /* Check for existence of an index file. */ + /*********************************************************************/ + if (sep) { + // Indexes are save in separate files +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + for (; pxdf; pxdf = pxdf->GetNext()) { + _splitpath(Ofn, drive, direc, fname, NULL); + strcat(strcat(fname, "_"), pxdf->GetName()); + _makepath(filename, drive, direc, fname, ftype); + PlugSetPath(filename, filename, GetPath()); +#if defined(WIN32) + rc |= !DeleteFile(filename); +#else // UNIX + rc |= remove(filename); +#endif // UNIX + } // endfor pxdf + + } else { // !sep + // Drop all indexes, delete the common file + PlugSetPath(filename, Ofn, GetPath()); + strcat(PlugRemoveType(filename, filename), ftype); +#if defined(WIN32) + rc = !DeleteFile(filename); +#else // UNIX + rc = remove(filename); +#endif // UNIX + } // endif sep + + if (rc) + sprintf(g->Message, MSG(DEL_FILE_ERR), filename); + + return rc; // Return true if error + } // end of DeleteIndexFile + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char buf[8]; + bool map = (am && (*am == 'M' || *am == 'm')); + LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F" + : (am && (*am == 'B' || *am == 'b')) ? "B" + : (am && !stricmp(am, "DBF")) ? "D" : "V"; + + Desc = Fn = Cat->GetStringCatInfo(g, "Filename", NULL); + Ofn = Cat->GetStringCatInfo(g, "Optname", Fn); + Cat->GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); + Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : + (toupper(*buf) == 'B') ? RECFM_BIN : + (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR; + Lrecl = Cat->GetIntCatInfo("Lrecl", 0); + + if (Recfm != RECFM_DBF) + Compressed = Cat->GetIntCatInfo("Compressed", 0); + + Mapped = Cat->GetBoolCatInfo("Mapped", map); + Block = Cat->GetIntCatInfo("Blocks", 0); + Last = Cat->GetIntCatInfo("Last", 0); + Ending = Cat->GetIntCatInfo("Ending", CRLF); + + if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) { + Huge = Cat->GetBoolCatInfo("Huge", Cat->GetDefHuge()); + Padded = Cat->GetBoolCatInfo("Padded", false); + Blksize = Cat->GetIntCatInfo("Blksize", 0); + Eof = (Cat->GetIntCatInfo("EOF", 0) != 0); + } else if (Recfm == RECFM_DBF) { + Maxerr = Cat->GetIntCatInfo("Maxerr", 0); + Accept = (Cat->GetIntCatInfo("Accept", 0) != 0); + ReadMode = Cat->GetIntCatInfo("Readmode", 0); + } else // (Recfm == RECFM_VAR) + AvgLen = Cat->GetIntCatInfo("Avglen", 0); + + // Ignore wrong Index definitions for catalog commands + return (Cat->GetIndexInfo(g, this) /*&& !Cat->GetCatFnc()*/); + } // end of DefineAM + +/***********************************************************************/ +/* InvalidateIndex: mark all indexes as invalid. */ +/***********************************************************************/ +bool DOSDEF::InvalidateIndex(PGLOBAL g) + { + if (To_Indx) + for (PIXDEF xp = To_Indx; xp; xp = xp->Next) + xp->Invalid = true; + + return false; + } // end of InvalidateIndex + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) + { + // Mapping not used for insert + USETEMP tmp = PlgGetUser(g)->UseTemp; + bool map = Mapped && mode != MODE_INSERT && + !(tmp != TMP_NO && Recfm == RECFM_VAR + && mode == MODE_UPDATE) && + !(tmp == TMP_FORCE && + (mode == MODE_UPDATE || mode == MODE_DELETE)); + PTXF txfp; + PTDBASE tdbp; + + /*********************************************************************/ + /* Allocate table and file processing class of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + if (Recfm == RECFM_DBF) { + if (Catfunc == FNC_NO) { + if (map) + txfp = new(g) DBMFAM(this); + else + txfp = new(g) DBFFAM(this); + + tdbp = new(g) TDBFIX(this, txfp); + } else // Catfunc should be 'C' + tdbp = new(g) TDBDCL(this); + + } else if (Recfm != RECFM_VAR && Compressed < 2) { + if (Huge) + txfp = new(g) BGXFAM(this); + else if (map) + txfp = new(g) MPXFAM(this); +#if defined(ZIP_SUPPORT) + else if (Compressed) + txfp = new(g) ZIXFAM(this); +#endif // ZIP_SUPPORT + else + txfp = new(g) FIXFAM(this); + + tdbp = new(g) TDBFIX(this, txfp); + } else { +#if defined(ZIP_SUPPORT) + if (Compressed) { + if (Compressed == 1) + txfp = new(g) ZIPFAM(this); + else { + strcpy(g->Message, "Compress 2 not supported yet"); +// txfp = new(g) ZLBFAM(defp); + return NULL; + } // endelse + + } else +#endif // ZIP_SUPPORT + if (map) + txfp = new(g) MAPFAM(this); + else + txfp = new(g) DOSFAM(this); + + // Txfp must be set even for not multiple tables because + // it is needed when calling Cardinality in GetBlockValues. + tdbp = new(g) TDBDOS(this, txfp); + } // endif Recfm + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); + + return tdbp; + } // end of GetTable + +/* ------------------------ Class TDBDOS ----------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBDOS class. This is the common class that */ +/* contain all that is common between the TDBDOS and TDBMAP classes. */ +/***********************************************************************/ +TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp) + { + if ((Txfp = txfp)) + Txfp->SetTdbp(this); + + Lrecl = tdp->Lrecl; + AvgLen = tdp->AvgLen; + Ftype = tdp->Recfm; + To_Line = NULL; + Cardinal = -1; + } // end of TDBDOS standard constructor + +TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp) + { + Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp; + Lrecl = tdbp->Lrecl; + AvgLen = tdbp->AvgLen; + Ftype = tdbp->Ftype; + To_Line = tdbp->To_Line; + Cardinal = tdbp->Cardinal; + } // end of TDBDOS copy constructor + +// Method +PTDB TDBDOS::CopyOne(PTABS t) + { + PTDB tp; + PDOSCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBDOS(g, this); + + for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) { + cp2 = new(g) DOSCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate DOS column description block. */ +/***********************************************************************/ +PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) DOSCOL(g, cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* Print debug information. */ +/***********************************************************************/ +void TDBDOS::PrintAM(FILE *f, char *m) + { + fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); + + if (Txfp->To_File) + fprintf(f, "%s File: %s\n", m, Txfp->To_File); + + } // end of PrintAM + +/***********************************************************************/ +/* Remake the indexes after the table was modified. */ +/***********************************************************************/ +int TDBDOS::ResetTableOpt(PGLOBAL g, bool dox) + { + int rc = RC_OK; + + MaxSize = -1; // Size must be recalculated + Cardinal = -1; // as well as Cardinality + + if (dox) { + // Remake eventual indexes + if (Mode != MODE_UPDATE) + To_SetCols = NULL; // Only used on Update + + Columns = NULL; // Not used anymore + Txfp->Reset(); // New start + Use = USE_READY; // So the table can be reopened + Mode = MODE_READ; // New mode + + if (!(PlgGetUser(g)->Check & CHK_OPT)) { + // After the table was modified the indexes + // are invalid and we should mark them as such... + rc = ((PDOSDEF)To_Def)->InvalidateIndex(g); + } else + // ... or we should remake them. + rc = MakeIndex(g, NULL, false); + + } // endif dox + + return rc; + } // end of ResetTableOpt + +/***********************************************************************/ +/* Check whether we have to create/update permanent indexes. */ +/***********************************************************************/ +int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add) + { + int k, n; + bool fixed, doit, sep, b = (pxdf != NULL); + PCOL *keycols, colp; + PIXDEF xdp, sxp = NULL; + PKPDEF kdp; + PDOSDEF dfp; +//PCOLDEF cdp; + PXINDEX x; + PXLOAD pxp; + PCATLG cat = PlgGetCatalog(g); + + Mode = MODE_READ; + Use = USE_READY; + dfp = (PDOSDEF)To_Def; + fixed = Cardinality(g) >= 0; + + // Are we are called from CreateTable or CreateIndex? + if (pxdf) { + if (!add && dfp->GetIndx()) { + strcpy(g->Message, MSG(INDX_EXIST_YET)); + return RC_FX; + } // endif To_Indx + + if (add && dfp->GetIndx()) { + for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext()) + if (!stricmp(sxp->GetName(), pxdf->GetName())) { + sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name); + return RC_FX; + } else if (!sxp->GetNext()) + break; + + sxp->SetNext(pxdf); +// first = false; + } else + dfp->SetIndx(pxdf); + +// pxdf->SetDef(dfp); + } else if (!(pxdf = dfp->GetIndx())) + return RC_INFO; // No index to make + + // Allocate all columns that will be used by indexes. + // This must be done before opening the table so specific + // column initialization can be done ( in particular by TDBVCT) + for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext()) + for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { + if (!(colp = ColDB(g, kdp->GetName(), 0))) { + sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name); + goto err; + } // endif colp + + colp->InitValue(g); + n = max(n, xdp->GetNparts()); + } // endfor kdp + + keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL)); + sep = cat->GetBoolCatInfo("SepIndex", false); + + /*********************************************************************/ + /* Construct and save the defined indexes. */ + /*********************************************************************/ + for (xdp = pxdf; xdp; xdp = xdp->GetNext()) + if (!OpenDB(g)) { + if (xdp->IsAuto() && fixed) + // Auto increment key and fixed file: use an XXROW index + continue; // XXROW index doesn't need to be made + + // On Update, redo only indexes that are modified + doit = !To_SetCols; + n = 0; + + if (sxp) + xdp->SetID(sxp->GetID() + 1); + + for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) { + // Check whether this column was updated + for (colp = To_SetCols; !doit && colp; colp = colp->GetNext()) + if (!stricmp(kdp->GetName(), colp->GetName())) + doit = true; + + keycols[n++] = ColDB(g, kdp->GetName(), 0); + } // endfor kdp + + // If no indexed columns were updated, don't remake the index + // if indexes are in separate files. + if (!doit && sep) + continue; + + k = xdp->GetNparts(); + + // Make the index and save it + if (dfp->Huge) + pxp = new(g) XHUGE; + else + pxp = new(g) XFILE; + + if (k == 1) // Simple index + x = new(g) XINDXS(this, xdp, pxp, keycols); + else // Multi-Column index + x = new(g) XINDEX(this, xdp, pxp, keycols); + + if (!x->Make(g, sxp)) { + // Retreive define values from the index + xdp->SetMaxSame(x->GetMaxSame()); +// xdp->SetSize(x->GetSize()); + + // store KXYCOL Mxs in KPARTDEF Mxsame + xdp->SetMxsame(x); + +#if defined(TRACE) + printf("Make done...\n"); +#endif // TRACE + +// if (x->GetSize() > 0) + sxp = xdp; + + xdp->SetInvalid(false); + } else + goto err; + + } else + return RC_INFO; // Error or Physical table does not exist + + if (Use == USE_OPEN) + CloseDB(g); + + return RC_OK; + +err: + if (sxp) + sxp->SetNext(NULL); + else + dfp->SetIndx(NULL); + + return RC_FX; + } // end of MakeIndex + +/***********************************************************************/ +/* DOS GetProgMax: get the max value for progress information. */ +/***********************************************************************/ +int TDBDOS::GetProgMax(PGLOBAL g) + { + return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g); + } // end of GetProgMax + +/***********************************************************************/ +/* DOS GetProgCur: get the current value for progress information. */ +/***********************************************************************/ +int TDBDOS::GetProgCur(void) + { + return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos(); + } // end of GetProgCur + +/***********************************************************************/ +/* RowNumber: return the ordinal number of the current row. */ +/***********************************************************************/ +int TDBDOS::RowNumber(PGLOBAL g, bool b) + { + if (To_Kindex) { + /*******************************************************************/ + /* Don't know how to retrieve RowID from file address. */ + /*******************************************************************/ + sprintf(g->Message, MSG(NO_ROWID_FOR_AM), + GetAmName(g, Txfp->GetAmType())); + return 0; + } else + return Txfp->GetRowID(); + + } // end of RowNumber + +/***********************************************************************/ +/* DOS Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int TDBDOS::Cardinality(PGLOBAL g) + { + if (!g) + return Txfp->Cardinality(g); + + if (Cardinal < 0) + Cardinal = Txfp->Cardinality(g); + + return Cardinal; + } // end of Cardinality + +/***********************************************************************/ +/* DOS GetMaxSize: returns file size estimate in number of lines. */ +/* This function covers variable record length files. */ +/***********************************************************************/ +int TDBDOS::GetMaxSize(PGLOBAL g) + { + if (MaxSize >= 0) + return MaxSize; + + if (!Cardinality(NULL)) { + int len = GetFileLength(g); + + if (len >= 0) { + if (trace) + htrc("Estimating lines len=%d ending=%d\n", + len, ((PDOSDEF)To_Def)->Ending); + + /*****************************************************************/ + /* Estimate the number of lines in the table (if not known) by */ + /* dividing the file length by the minimum line length assuming */ + /* only the last column can be of variable length. This will be */ + /* a ceiling estimate (as last column is never totally absent). */ + /*****************************************************************/ + int rec = ((PDOSDEF)To_Def)->Ending; // +2: CRLF +1: LF + + if (AvgLen <= 0) // No given average estimate + rec += EstimatedLength(g); + else // A lower estimate was given for the average record length + rec += (int)AvgLen; + + if (trace) + htrc(" Filen=%d min_rec=%d\n", len, rec); + + MaxSize = (len + rec - 1) / rec; + + if (trace) + htrc(" Estimated max_K=%d\n", MaxSize); + + } // endif len + + } else + MaxSize = Cardinality(g); + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* DOS EstimatedLength. Returns an estimated minimum line length. */ +/***********************************************************************/ +int TDBDOS::EstimatedLength(PGLOBAL g) + { + int dep = 0; + PCOLDEF cdp = To_Def->GetCols(); + + if (!cdp->GetNext()) { + // One column table, we are going to return a ridiculous + // result if we set dep to 1 + dep = 1 + cdp->GetLong() / 20; // Why 20 ????? + } else for (; cdp; cdp = cdp->GetNext()) + dep = max(dep, cdp->GetOffset()); + + return (int)dep; + } // end of Estimated Length + +/***********************************************************************/ +/* DOS tables favor the use temporary files for Update. */ +/***********************************************************************/ +bool TDBDOS::IsUsingTemp(PGLOBAL g) + { + USETEMP usetemp = PlgGetUser(g)->UseTemp; + + return (usetemp == TMP_YES || usetemp == TMP_FORCE || + (usetemp == TMP_AUTO && Mode == MODE_UPDATE)); + } // end of IsUsingTemp + +/***********************************************************************/ +/* DOS Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBDOS::OpenDB(PGLOBAL g) + { + if (trace) + htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n", + this, Tdb_No, Use, Mode); + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + Txfp->Rewind(); // see comment in Work.log + + if (SkipHeader(g)) + return true; + + return false; + } // endif use + + if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS) { + // Delete all lines. Not handled in MAP or block mode + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + Txfp->SetTdbp(this); + } else if (Txfp->Blocked && (Mode == MODE_DELETE || + (Mode == MODE_UPDATE && PlgGetUser(g)->UseTemp != TMP_NO))) { + /*******************************************************************/ + /* Delete is not currently handled in block mode neither Update */ + /* when using a temporary file. */ + /*******************************************************************/ + if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE) + Txfp = new(g) MAPFAM((PDOSDEF)To_Def); +#if defined(ZIP_SUPPORT) + else if (Txfp->GetAmType() == TYPE_AM_ZIP) + Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); +#endif // ZIP_SUPPORT + else if (Txfp->GetAmType() != TYPE_AM_DOS) + Txfp = new(g) DOSFAM((PDOSDEF)To_Def); + + Txfp->SetTdbp(this); + } // endif Mode + + /*********************************************************************/ + /* Open according to logical input/output mode required. */ + /* Use conventionnal input/output functions. */ + /* Treat files as binary in Delete mode (for line moving) */ + /*********************************************************************/ + if (Txfp->OpenTableFile(g)) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Allocate the line buffer plus a null character. */ + /*********************************************************************/ + To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1); + + if (Mode == MODE_INSERT) { + // Spaces between fields must be filled with blanks + memset(To_Line, ' ', Lrecl); + To_Line[Lrecl] = '\0'; + } else + memset(To_Line, 0, Lrecl + 1); + + if (trace) + htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line); + + if (SkipHeader(g)) // When called from CSV/FMT files + return true; + + /*********************************************************************/ + /* Reset statistics values. */ + /*********************************************************************/ + num_read = num_there = num_eq[0] = num_eq[1] = 0; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for DOS access method. */ +/***********************************************************************/ +int TDBDOS::ReadDB(PGLOBAL g) + { + if (trace > 1) + htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n", + GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line); + + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + num_there++; + return RC_OK; + default: + /***************************************************************/ + /* Set the file position according to record to read. */ + /***************************************************************/ + if (SetRecpos(g, recpos)) + return RC_FX; + + if (trace > 1) + htrc("File position is now %d\n", GetRecpos()); + + if (Mode == MODE_READ) + /*************************************************************/ + /* Defer physical reading until one column setting needs it */ + /* as it can be a big saving on joins where no other column */ + /* than the keys are used, so reading is unnecessary. */ + /*************************************************************/ + if (Txfp->DeferReading()) + return RC_OK; + + } // endswitch recpos + + } // endif To_Kindex + + if (trace > 1) + htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line); + + /*********************************************************************/ + /* Now start the reading process. */ + /*********************************************************************/ + return ReadBuffer(g); + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for DOS access method. */ +/***********************************************************************/ +int TDBDOS::WriteDB(PGLOBAL g) + { + if (trace > 1) + htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode); + + if (!Ftype && (Mode == MODE_INSERT || Txfp->GetUseTemp())) { + char *p; + + /*******************************************************************/ + /* Suppress trailing blanks. */ + /* Also suppress eventual null from last line. */ + /*******************************************************************/ + for (p = To_Line + Lrecl -1; p >= To_Line; p--) + if (*p && *p != ' ') + break; + + *(++p) = '\0'; + } // endif Mode + + if (trace > 1) + htrc("Write: line is='%s'\n", To_Line); + + // Now start the writing process + return Txfp->WriteBuffer(g); + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for DOS (and FIX) access method. */ +/* RC_FX means delete all. Nothing to do here (was done at open). */ +/***********************************************************************/ +int TDBDOS::DeleteDB(PGLOBAL g, int irc) + { + return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc); + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for DOS access method. */ +/***********************************************************************/ +void TDBDOS::CloseDB(PGLOBAL g) + { + if (To_Kindex) { + To_Kindex->Close(); + To_Kindex = NULL; + } // endif + + Txfp->CloseTableFile(g); + } // end of CloseDB + +// ------------------------ DOSCOL functions ---------------------------- + +/***********************************************************************/ +/* DOSCOL public constructor (also called by MAPCOL). */ +/***********************************************************************/ +DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am) + : COLBLK(cdp, tp, i) + { + char *p; + int prec = Format.Prec; + PTXF txfp = ((PTDBDOS)tp)->Txfp; + + assert(cdp); + + if (cp) { + Next = cp->GetNext(); + cp->SetNext(this); + } else { + Next = tp->GetColumns(); + tp->SetColumns(this); + } // endif cprec + + // Set additional Dos access method information for column. + Deplac = cdp->GetOffset(); + Long = cdp->GetLong(); + To_Val = NULL; + + OldVal = NULL; // Currently used only in MinMax + Ldz = false; + Nod = false; + Dcm = -1; + p = cdp->GetFmt(); + + if (p && IsTypeNum(Buf_Type)) { + // Formatted numeric value + for (; p && *p && isalpha(*p); p++) + switch (toupper(*p)) { + case 'Z': // Have leading zeros + Ldz = true; + break; + case 'N': // Have no decimal point + Nod = true; + break; + } // endswitch p + + // Set number of decimal digits + Dcm = (*p) ? atoi(p) : GetPrecision(); + } // endif fmt + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of DOSCOL constructor + +/***********************************************************************/ +/* DOSCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Deplac = col1->Deplac; + Long = col1->Long; + To_Val = col1->To_Val; + Ldz = col1->Ldz; + Nod = col1->Nod; + Dcm = col1->Dcm; + OldVal = col1->OldVal; + Buf = col1->Buf; + } // end of DOSCOL copy constructor + +/***********************************************************************/ +/* VarSize: This function tells UpdateDB whether or not the block */ +/* optimization file must be redone if this column is updated, even */ +/* it is not sorted or clustered. This applies to the last column of */ +/* a variable length table that is blocked, because if it is updated */ +/* using a temporary file, the block size may be modified. */ +/***********************************************************************/ +bool DOSCOL::VarSize(void) + { + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + PTXF txfp = tdbp->Txfp; + + if (Cdp && !Cdp->GetNext() // Must be the last column + && tdbp->Ftype == RECFM_VAR // of a DOS variable length + && txfp->Blocked // blocked table + && txfp->GetUseTemp()) // using a temporary file. + return true; + else + return false; + + } // end VarSize + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_FLOAT) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetPrecision()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Allocate the buffer used in WriteColumn for numeric columns + if (IsTypeNum(Buf_Type)) + Buf = (char*)PlugSubAlloc(g, NULL, max(32, Long + Dcm + 1)); + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the last line */ +/* read from the corresponding table, extract from it the field */ +/* corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void DOSCOL::ReadColumn(PGLOBAL g) + { + char *p; + int i, rc; + int field; + double dval; + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + + if (trace > 1) + htrc( + "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n", + Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type); + + /*********************************************************************/ + /* If physical reading of the line was deferred, do it now. */ + /*********************************************************************/ + if (!tdbp->IsRead()) + if ((rc = tdbp->ReadBuffer(g)) != RC_OK) { + if (rc == RC_EF) + sprintf(g->Message, MSG(INV_DEF_READ), rc); + + longjmp(g->jumper[g->jump_level], 11); + } // endif + + p = tdbp->To_Line + Deplac; + field = Long; + + switch (tdbp->Ftype) { + case RECFM_VAR: + /*****************************************************************/ + /* For a variable length file, check if the field exists. */ + /*****************************************************************/ + if (strlen(tdbp->To_Line) < (unsigned)Deplac) + field = 0; + + case RECFM_FIX: // Fixed length text file + case RECFM_DBF: // Fixed length DBase file + if (Nod) switch (Buf_Type) { + case TYPE_INT: + case TYPE_SHORT: + case TYPE_TINY: + case TYPE_BIGINT: + Value->SetValue_char(p, field - Dcm); + break; + case TYPE_FLOAT: + Value->SetValue_char(p, field); + dval = Value->GetFloatValue(); + + for (i = 0; i < Dcm; i++) + dval /= 10.0; + + Value->SetValue(dval); + break; + default: + Value->SetValue_char(p, field); + break; + } // endswitch Buf_Type + + else + Value->SetValue_char(p, field); + + break; + default: + sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype); + longjmp(g->jumper[g->jump_level], 34); + } // endswitch Ftype + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void DOSCOL::WriteColumn(PGLOBAL g) + { + char *p, *p2, fmt[32]; + int i, k, len, field; + PTDBDOS tdbp = (PTDBDOS)To_Tdb; + + if (trace > 1) + htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, tdbp->GetTdb_No(), ColUse, Status); + + p = tdbp->To_Line + Deplac; + + if (trace > 1) + htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long); + + field = Long; + + if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) { + len = (signed)strlen(tdbp->To_Line); + + if (tdbp->IsUsingTemp(g)) + // Because of eventual missing field(s) the buffer must be reset + memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len); + else + // The size actually available must be recalculated + field = min(len - Deplac, Long); + + } // endif Ftype + + if (trace > 1) + htrc("Long=%d field=%d coltype=%d colval=%p\n", + Long, field, Buf_Type, Value); + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + /*********************************************************************/ + /* This test is only useful for compressed(2) tables. */ + /*********************************************************************/ + if (tdbp->Ftype != RECFM_BIN) { + if (Ldz || Nod || Dcm >= 0) { + switch (Buf_Type) { + case TYPE_SHORT: + strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd"); + i = 0; + + if (Nod) + for (; i < Dcm; i++) + strcat(fmt, "0"); + + len = sprintf(Buf, fmt, field - i, Value->GetShortValue()); + break; + case TYPE_INT: + strcpy(fmt, (Ldz) ? "%0*d" : "%*.d"); + i = 0; + + if (Nod) + for (; i < Dcm; i++) + strcat(fmt, "0"); + + len = sprintf(Buf, fmt, field - i, Value->GetIntValue()); + break; + case TYPE_TINY: + strcpy(fmt, (Ldz) ? "%0*d" : "%*.d"); + i = 0; + + if (Nod) + for (; i < Dcm; i++) + strcat(fmt, "0"); + + len = sprintf(Buf, fmt, field - i, Value->GetTinyValue()); + break; + case TYPE_FLOAT: + strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf"); + sprintf(Buf, fmt, field + ((Nod && Dcm) ? 1 : 0), + Dcm, Value->GetFloatValue()); + len = strlen(Buf); + + if (Nod && Dcm) + for (i = k = 0; i < len; i++, k++) + if (Buf[i] != ' ') { + if (Buf[i] == '.' || Buf[i] == ',') + k++; + + Buf[i] = Buf[k]; + } // endif Buf(i) + + len = strlen(Buf); + break; + } // endswitch BufType + + p2 = Buf; + } else // Standard PlugDB format + p2 = Value->ShowValue(Buf, field); + + if (trace) + htrc("new length(%p)=%d\n", p2, strlen(p2)); + + if ((len = strlen(p2)) > field) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p2, Name, field); + longjmp(g->jumper[g->jump_level], 31); + } // endif + + if (trace > 1) + htrc("buffer=%s\n", p2); + + /*******************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*******************************************************************/ + if (Status) { + memset(p, ' ', field); + memcpy(p, p2, len); + + if (trace > 1) + htrc(" col write: '%.*s'\n", len, p); + + } // endif Use + + } else // BIN compressed table + /*******************************************************************/ + /* Check if updating is Ok, meaning col value is not too long. */ + /* Updating to be done only during the second pass (Status=true) */ + /*******************************************************************/ + if (Value->GetBinValue(p, Long, Status)) { + sprintf(g->Message, MSG(BIN_F_TOO_LONG), + Name, Value->GetSize(), Long); + longjmp(g->jumper[g->jump_level], 31); + } // endif + + } // end of WriteColumn + +/***********************************************************************/ +/* Make file output of a Dos column descriptor block. */ +/***********************************************************************/ +void DOSCOL::Print(PGLOBAL g, FILE *f, uint n) + { + COLBLK::Print(g, f, n); + } // end of Print + +/* ------------------------------------------------------------------- */ + diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h new file mode 100644 index 00000000000..5f67ffad92f --- /dev/null +++ b/storage/connect/tabdos.h @@ -0,0 +1,247 @@ +/*************** TabDos H Declares Source Code File (.H) ***************/ +/* Name: TABDOS.H Version 3.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2012 */ +/* */ +/* This file contains the DOS classes declares. */ +/***********************************************************************/ + +#ifndef __TABDOS_H +#define __TABDOS_H + +#include "xtable.h" // Table base class declares +#include "colblk.h" // Column base class declares +#include "xindex.h" + +//pedef struct _tabdesc *PTABD; // For friend setting +typedef class TXTFAM *PTXF; + +/***********************************************************************/ +/* DOS table. */ +/***********************************************************************/ +class DllExport DOSDEF : public TABDEF { /* Logical table description */ + friend class OEMDEF; + friend class TDBDOS; + friend class TDBFIX; + friend class TXTFAM; + friend class DBFBASE; + public: + // Constructor + DOSDEF(void); + + // Implementation + virtual AMT GetDefType(void) {return TYPE_AM_DOS;} + virtual const char *GetType(void) {return "DOS";} + virtual PIXDEF GetIndx(void) {return To_Indx;} + virtual void SetIndx(PIXDEF xdp) {To_Indx = xdp;} + PSZ GetFn(void) {return Fn;} + PSZ GetOfn(void) {return Ofn;} + void SetBlock(int block) {Block = block;} + int GetBlock(void) {return Block;} + int GetLast(void) {return Last;} + void SetLast(int last) {Last = last;} + int GetLrecl(void) {return Lrecl;} + void SetLrecl(int lrecl) {Lrecl = lrecl;} + bool GetPadded(void) {return Padded;} + bool GetEof(void) {return Eof;} + int GetBlksize(void) {return Blksize;} + int GetEnding(void) {return Ending;} + int *GetTo_Pos(void) {return To_Pos;} + virtual bool IsHuge(void) {return Huge;} + + // Methods + virtual bool DeleteTableFile(PGLOBAL g); + virtual bool Indexable(void) {return Compressed != 1;} + virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf); + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE mode); + bool InvalidateIndex(PGLOBAL g); + + protected: + virtual bool Erase(char *filename); + + // Members + PSZ Fn; /* Path/Name of corresponding file */ + PSZ Ofn; /* Base Path/Name of matching index files*/ + PIXDEF To_Indx; /* To index definitions blocks */ + RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */ + bool Mapped; /* 0: disk file, 1: memory mapped file */ + bool Padded; /* true for padded table file */ + bool Huge; /* true for files larger than 2GB */ + bool Accept; /* true if wrong lines are accepted (DBF)*/ + bool Eof; /* true if an EOF (0xA) character exists */ + int *To_Pos; /* To array of block starting positions */ + int Compressed; /* 0: No, 1: gz, 2:zlib compressed file */ + int Lrecl; /* Size of biggest record */ + int AvgLen; /* Average size of records */ + int Block; /* Number de blocks of FIX/VCT tables */ + int Last; /* Number of elements of last block */ + int Blksize; /* Size of padded blocks */ + int Maxerr; /* Maximum number of bad records (DBF) */ + int ReadMode; /* Specific to DBF */ + int Ending; /* Length of end of lines */ + }; // end of DOSDEF + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* that are standard files with columns starting at fixed offset. */ +/* The last column (and record) is of variable length. */ +/***********************************************************************/ +class DllExport TDBDOS : public TDBASE { +//friend class KINDEX; + friend class XINDEX; + friend class DOSCOL; + friend class MAPCOL; + friend class TXTFAM; + friend class DOSFAM; + friend class VCTCOL; +//friend class TDBMUL; + friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool); + public: + // Constructors + TDBDOS(PDOSDEF tdp, PTXF txfp); + TDBDOS(PGLOBAL g, PTDBDOS tdbp); + + // Inline functions + inline void SetTxfp(PTXF txfp) {Txfp = txfp; Txfp->SetTdbp(this);} + inline PTXF GetTxfp(void) {return Txfp;} + inline char *GetLine(void) {return To_Line;} + inline int GetCurBlk(void) {return Txfp->GetCurBlk();} + inline void SetLine(char *toline) {To_Line = toline;} + inline void IncLine(int inc) {To_Line += inc;} + inline bool IsRead(void) {return Txfp->IsRead;} + inline PXOB *GetLink(void) {return To_Link;} +//inline PCOL *GetKeyCol(void) {return To_Key_Col;} + + // Implementation + virtual AMT GetAmType(void) {return Txfp->GetAmType();} + virtual PSZ GetFile(PGLOBAL g) {return Txfp->To_File;} + virtual void SetFile(PGLOBAL g, PSZ fn) {Txfp->To_File = fn;} + virtual RECFM GetFtype(void) {return Ftype;} + virtual bool SkipHeader(PGLOBAL g) {return false;} + virtual void RestoreNrec(void) {Txfp->SetNrec(1);} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBDOS(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual void ResetDB(void) {Txfp->Reset();} + virtual bool IsUsingTemp(PGLOBAL g); +//virtual bool NeedIndexing(PGLOBAL g); + virtual void ResetSize(void) {MaxSize = Cardinal = -1;} + virtual int ResetTableOpt(PGLOBAL g, bool dox); +//virtual int MakeBlockValues(PGLOBAL g); +//virtual bool SaveBlockValues(PGLOBAL g); +//virtual bool GetBlockValues(PGLOBAL g); +//virtual PBF InitBlockFilter(PGLOBAL g, PFIL filp); +//virtual PBX InitBlockIndex(PGLOBAL g); +//virtual int TestBlock(PGLOBAL g); + virtual void PrintAM(FILE *f, char *m); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual char *GetOpenMode(PGLOBAL g, char *opmode) {return NULL;} + virtual int GetFileLength(PGLOBAL g) {return Txfp->GetFileLength(g);} + virtual int GetProgMax(PGLOBAL g); + virtual int GetProgCur(void); + virtual int GetAffectedRows(void) {return Txfp->GetDelRows();} + virtual int GetRecpos(void) {return Txfp->GetPos();} + virtual bool SetRecpos(PGLOBAL g, int recpos) + {return Txfp->SetPos(g, recpos);} + virtual int RowNumber(PGLOBAL g, bool b = false); + virtual int Cardinality(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g) {return Txfp->ReadBuffer(g);} + + // Specific routine + virtual int EstimatedLength(PGLOBAL g); + + // Optimization routines +// void ResetBlockFilter(PGLOBAL g); + int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add); +// bool GetDistinctColumnValues(PGLOBAL g, int nrec); + + protected: +// PBF CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv); + + // Members + PTXF Txfp; // To the File access method class +//PBX To_BlkIdx; // To index test block +//PBF To_BlkFil; // To evaluation block filter +//PFIL SavFil; // Saved hidden filter + char *To_Line; // Points to current processed line + int Cardinal; // Table Cardinality + RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT) + int Lrecl; // Logical Record Length + int AvgLen; // Logical Record Average Length +//int Xeval; // BlockTest return value +//int Beval; // BlockEval return value + }; // end of class TDBDOS + +/***********************************************************************/ +/* Class DOSCOL: DOS access method column descriptor. */ +/* This A.M. is used for text file tables under operating systems */ +/* DOS, OS2, UNIX, WIN16 and WIN32. */ +/***********************************************************************/ +class DllExport DOSCOL : public COLBLK { + friend class TDBDOS; + friend class TDBFIX; + public: + // Constructors + DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am = "DOS"); + DOSCOL(DOSCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_DOS;} +//virtual int GetClustered(void) {return Clustered;} +//virtual int IsClustered(void) {return (Clustered && +// ((PDOSDEF)(((PTDBDOS)To_Tdb)->To_Def))->IsOptimized());} +//virtual int IsSorted(void) {return Sorted;} + virtual void SetTo_Val(PVAL valp) {To_Val = valp;} +//virtual PVBLK GetMin(void) {return Min;} +//virtual PVBLK GetMax(void) {return Max;} +//virtual int GetNdv(void) {return Ndv;} +//virtual int GetNbm(void) {return Nbm;} +//virtual PVBLK GetBmap(void) {return Bmap;} +//virtual PVBLK GetDval(void) {return Dval;} + + // Methods + virtual bool VarSize(void); + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + virtual void Print(PGLOBAL g, FILE *, uint); + + protected: +//virtual bool SetMinMax(PGLOBAL g); +//virtual bool SetBitMap(PGLOBAL g); +// bool CheckSorted(PGLOBAL g); +// bool AddDistinctValue(PGLOBAL g); + + // Default constructor not to be used + DOSCOL(void) {} + + // Members +//PVBLK Min; // Array of block min values +//PVBLK Max; // Array of block max values +//PVBLK Bmap; // Array of block bitmap values +//PVBLK Dval; // Array of column distinct values + PVAL To_Val; // To value used for Update/Insert + PVAL OldVal; // The previous value of the object. + char *Buf; // Buffer used in write operations + bool Ldz; // True if field contains leading zeros + bool Nod; // True if no decimal point + int Dcm; // Last Dcm digits are decimals +//int Clustered; // 0:No 1:Yes +//int Sorted; // 0:No 1:Asc (2:Desc - NIY) + int Deplac; // Offset in dos_buf +//int Ndv; // Number of distinct values +//int Nbm; // Number of uint in bitmap + }; // end of class DOSCOL + +#endif // __TABDOS_H diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp new file mode 100644 index 00000000000..801de79aad9 --- /dev/null +++ b/storage/connect/tabfix.cpp @@ -0,0 +1,502 @@ +/************* TabFix C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABFIX */ +/* ------------- */ +/* Version 4.8 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the TDBFIX class DB routines. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant section of system dependant header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/***********************************************************************/ +#include "global.h" // global declares +#include "plgdbsem.h" // DB application declares +#include "filamfix.h" +#include "filamdbf.h" +#include "tabfix.h" // TDBFIX, FIXCOL classes declares + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +extern "C" int trace; +extern int num_read, num_there, num_eq[2]; // Statistics +static const longlong M2G = 0x80000000; +static const longlong M4G = (longlong)2 * M2G; + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBFIX class. */ +/***********************************************************************/ +TDBFIX::TDBFIX(PDOSDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp) + { +//Cardinal = -1; + } // end of TDBFIX standard constructor + +TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp) + { +//Cardinal = tdbp->Cardinal; + } // end of TDBFIX copy constructor + +// Method +PTDB TDBFIX::CopyOne(PTABS t) + { + PTDB tp; + PGLOBAL g = t->G; + + tp = new(g) TDBFIX(g, this); + + if (Ftype < 2) { + // File is text + PDOSCOL cp1, cp2; + + for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) { + cp2 = new(g) DOSCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + } else { + // File is binary + PBINCOL cp1, cp2; + + for (cp1 = (PBINCOL)Columns; cp1; cp1 = (PBINCOL)cp1->GetNext()) { + cp2 = new(g) BINCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + } // endif Ftype + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void TDBFIX::ResetDB(void) + { + TDBDOS::ResetDB(); + } // end of ResetDB + +/***********************************************************************/ +/* Allocate FIX (DOS) or BIN column description block. */ +/***********************************************************************/ +PCOL TDBFIX::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + if (Ftype == RECFM_BIN) + return new(g) BINCOL(g, cdp, this, cprec, n); + else + return new(g) DOSCOL(g, cdp, this, cprec, n); + + } // end of MakeCol + +/***********************************************************************/ +/* Remake the indexes after the table was modified. */ +/***********************************************************************/ +int TDBFIX::ResetTableOpt(PGLOBAL g, bool dox) + { + RestoreNrec(); // May have been modified + return TDBDOS::ResetTableOpt(g, dox); + } // end of ResetTableOpt + +/***********************************************************************/ +/* Reset the Nrec and BlkSize values that can have been modified. */ +/***********************************************************************/ +void TDBFIX::RestoreNrec(void) + { + if (!Txfp->Padded) { + Txfp->Nrec = (To_Def && To_Def->GetElemt()) ? To_Def->GetElemt() + : DOS_BUFF_LEN; + Txfp->Blksize = Txfp->Nrec * Txfp->Lrecl; + } // endif Padded + + } // end of RestoreNrec + +/***********************************************************************/ +/* FIX Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int TDBFIX::Cardinality(PGLOBAL g) + { + if (!g) + return Txfp->Cardinality(g); + + if (Cardinal < 0) + Cardinal = Txfp->Cardinality(g); + + return Cardinal; + } // end of Cardinality + +/***********************************************************************/ +/* FIX GetMaxSize: returns file size in number of lines. */ +/***********************************************************************/ +int TDBFIX::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) + MaxSize = Cardinality(g); + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* FIX ResetSize: Must reset Headlen for DBF tables only. */ +/***********************************************************************/ +void TDBFIX::ResetSize(void) + { + if (Txfp->GetAmType() == TYPE_AM_DBF) + Txfp->Headlen = 0; + + MaxSize = Cardinal = -1; + } // end of ResetSize + +/***********************************************************************/ +/* FIX GetProgMax: get the max value for progress information. */ +/***********************************************************************/ +int TDBFIX::GetProgMax(PGLOBAL g) + { + return Cardinality(g); + } // end of GetProgMax + +/***********************************************************************/ +/* RowNumber: return the ordinal number of the current row. */ +/***********************************************************************/ +int TDBFIX::RowNumber(PGLOBAL g, bool b) + { + if (Txfp->GetAmType() == TYPE_AM_DBF) { + if (!b && To_Kindex) { + /*****************************************************************/ + /* Don't know how to retrieve Rows from DBF file address */ + /* because of eventual deleted lines still in the file. */ + /*****************************************************************/ + sprintf(g->Message, MSG(NO_ROWID_FOR_AM), + GetAmName(g, Txfp->GetAmType())); + return 0; + } // endif To_Kindex + + if (!b) + return Txfp->GetRows(); + + } // endif DBF + + return Txfp->GetRowID(); + } // end of RowNumber + +/***********************************************************************/ +/* FIX tables don't use temporary files except if specified as do it. */ +/***********************************************************************/ +bool TDBFIX::IsUsingTemp(PGLOBAL g) + { + USETEMP usetemp = PlgGetUser(g)->UseTemp; + + return (usetemp == TMP_YES || usetemp == TMP_FORCE); + } // end of IsUsingTemp + +/***********************************************************************/ +/* FIX Access Method opening routine (also used by the BIN a.m.) */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBFIX::OpenDB(PGLOBAL g) + { + if (trace) + htrc("FIX OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d Ftype=%d\n", + this, Tdb_No, Use, To_Key_Col, Mode, Ftype); + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + if (To_Kindex) + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + else + Txfp->Rewind(); // see comment in Work.log + + return false; + } // endif use + + if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() == TYPE_AM_MAP) { + // Delete all lines. Not handled in MAP mode + Txfp = new(g) FIXFAM((PDOSDEF)To_Def); + Txfp->SetTdbp(this); + } // endif Mode + + /*********************************************************************/ + /* Call Cardinality to calculate Block in the case of Func queries. */ + /* and also in the case of multiple tables. */ + /*********************************************************************/ + if (Cardinality(g) < 0) + return true; + + /*********************************************************************/ + /* Open according to required logical input/output mode. */ + /* Use conventionnal input/output functions. */ + /* Treat fixed length text files as binary. */ + /*********************************************************************/ + if (Txfp->OpenTableFile(g)) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Initialize To_Line at the beginning of the block buffer. */ + /*********************************************************************/ + To_Line = Txfp->GetBuf(); // For WriteDB + + if (trace) + htrc("OpenDos: R%hd mode=%d\n", Tdb_No, Mode); + + /*********************************************************************/ + /* Reset buffer access according to indexing and to mode. */ + /*********************************************************************/ + Txfp->ResetBuffer(g); + + /*********************************************************************/ + /* Reset statistics values. */ + /*********************************************************************/ + num_read = num_there = num_eq[0] = num_eq[1] = 0; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for FIX access method. */ +/***********************************************************************/ +int TDBFIX::WriteDB(PGLOBAL g) + { + return Txfp->WriteBuffer(g); + } // end of WriteDB + +// ------------------------ BINCOL functions ---------------------------- + +/***********************************************************************/ +/* BINCOL public constructor. */ +/***********************************************************************/ +BINCOL::BINCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am) + : DOSCOL(g, cdp, tp, cp, i, am) + { + Fmt = (cdp->GetFmt()) ? toupper(*cdp->GetFmt()) : 'X'; + } // end of BINCOL constructor + +/***********************************************************************/ +/* FIXCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +BINCOL::BINCOL(BINCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp) + { + Fmt = col1->Fmt; + } // end of BINCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the last line */ +/* read from the corresponding table and extract from it the field */ +/* corresponding to this column. */ +/***********************************************************************/ +void BINCOL::ReadColumn(PGLOBAL g) + { + char *p; + int rc; + PTDBFIX tdbp = (PTDBFIX)To_Tdb; + + if (trace) + htrc("BIN ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n", + Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type); + + /*********************************************************************/ + /* If physical reading of the line was deferred, do it now. */ + /*********************************************************************/ + if (!tdbp->IsRead()) + if ((rc = tdbp->ReadBuffer(g)) != RC_OK) { + if (rc == RC_EF) + sprintf(g->Message, MSG(INV_DEF_READ), rc); + + longjmp(g->jumper[g->jump_level], 11); + } // endif + + p = tdbp->To_Line + Deplac; + + /*********************************************************************/ + /* Set Value from the line field. */ + /*********************************************************************/ + switch (Fmt) { + case 'X': // Standard not converted values + Value->SetBinValue(p); + break; + case 'S': // Short integer + Value->SetValue((int)*(short*)p); + break; + case 'T': // Tiny integer + Value->SetValue((int)*p); + break; + case 'L': // Long Integer + strcpy(g->Message, "Format L is deprecated, use I"); + longjmp(g->jumper[g->jump_level], 11); + case 'I': // Integer + Value->SetValue(*(int*)p); + break; + case 'F': // Float + case 'R': // Real + Value->SetValue((double)*(float*)p); + break; + case 'D': // Double + Value->SetValue(*(double*)p); + break; + case 'C': // Text + Value->SetValue_char(p, Long); + break; + default: + sprintf(g->Message, MSG(BAD_BIN_FMT), Fmt, Name); + longjmp(g->jumper[g->jump_level], 11); + } // endswitch Fmt + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer. */ +/***********************************************************************/ +void BINCOL::WriteColumn(PGLOBAL g) + { + char *p, *s; + longlong n; + PTDBFIX tdbp = (PTDBFIX)To_Tdb; + + if (trace) { + htrc("BIN WriteColumn: col %s R%d coluse=%.4X status=%.4X", + Name, tdbp->GetTdb_No(), ColUse, Status); + htrc(" Lrecl=%d\n", tdbp->Lrecl); + htrc("Long=%d deplac=%d coltype=%d ftype=%c\n", + Long, Deplac, Buf_Type, *Format.Type); + } // endif trace + + /*********************************************************************/ + /* Check whether the new value has to be converted to Buf_Type. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + p = tdbp->To_Line + Deplac; + + /*********************************************************************/ + /* Check whether updating is Ok, meaning col value is not too long. */ + /* Updating will be done only during the second pass (Status=true) */ + /* Conversion occurs if the external format Fmt is specified. */ + /*********************************************************************/ + switch (Fmt) { + case 'X': + // Standard not converted values + if (Value->GetBinValue(p, Long, Status)) { + sprintf(g->Message, MSG(BIN_F_TOO_LONG), + Name, Value->GetSize(), Long); + longjmp(g->jumper[g->jump_level], 31); + } // endif Fmt + + break; + case 'S': // Short integer + n = Value->GetBigintValue(); + + if (n > 32767LL || n < -32768LL) { + sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name); + longjmp(g->jumper[g->jump_level], 31); + } else if (Status) + *(short *)p = (short)n; + + break; + case 'T': // Tiny integer + n = Value->GetBigintValue(); + + if (n > 255LL || n < -256LL) { + sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name); + longjmp(g->jumper[g->jump_level], 31); + } else if (Status) + *p = (char)n; + + break; + case 'L': // Long Integer + strcpy(g->Message, "Format L is deprecated, use I"); + longjmp(g->jumper[g->jump_level], 11); + case 'I': // Integer + n = Value->GetBigintValue(); + + if (n > INT_MAX || n < INT_MIN) { + sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name); + longjmp(g->jumper[g->jump_level], 31); + } else if (Status) + *(int *)p = Value->GetIntValue(); + + break; + case 'B': // Large (big) integer + if (Status) + *(longlong *)p = (longlong)Value->GetBigintValue(); + + break; + case 'F': // Float + case 'R': // Real + if (Status) + *(float *)p = (float)Value->GetFloatValue(); + + break; + case 'D': // Double + if (Status) + *(double *)p = Value->GetFloatValue(); + + break; + case 'C': // Characters + if ((n = (signed)strlen(Value->GetCharString(Buf))) > Long) { + sprintf(g->Message, MSG(BIN_F_TOO_LONG), Name, (int) n, Long); + longjmp(g->jumper[g->jump_level], 31); + } // endif n + + if (Status) { + s = Value->GetCharString(Buf); + memset(p, ' ', Long); + memcpy(p, s, strlen(s)); + } // endif Status + + break; + default: + sprintf(g->Message, MSG(BAD_BIN_FMT), Fmt, Name); + longjmp(g->jumper[g->jump_level], 11); + } // endswitch Fmt + + } // end of WriteColumn + +/* ------------------------ End of TabFix ---------------------------- */ diff --git a/storage/connect/tabfix.h b/storage/connect/tabfix.h new file mode 100644 index 00000000000..bcd171b37bb --- /dev/null +++ b/storage/connect/tabfix.h @@ -0,0 +1,99 @@ +/*************** TabDos H Declares Source Code File (.H) ***************/ +/* Name: TABFIX.H Version 2.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2012 */ +/* */ +/* This file contains the TDBFIX and (FIX/BIN)COL classes declares. */ +/***********************************************************************/ +#ifndef __TABFIX__ +#define __TABFIX__ +#include "tabdos.h" /* Base class declares */ +#include "filamdbf.h" + +typedef class FIXCOL *PFIXCOL; +typedef class BINCOL *PBINCOL; +typedef class TXTFAM *PTXF; + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* that are standard files with columns starting at fixed offset. */ +/* This class is for fixed formatted files. */ +/***********************************************************************/ +class DllExport TDBFIX : public TDBDOS { + friend class FIXCOL; + friend class BINCOL; + public: + // Constructor + TDBFIX(PDOSDEF tdp, PTXF txfp); + TDBFIX(PGLOBAL g, PTDBFIX tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_FIX;} + virtual void RestoreNrec(void); + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBFIX(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual void ResetDB(void); + virtual bool IsUsingTemp(PGLOBAL g); + virtual int RowNumber(PGLOBAL g, bool b = false); + virtual int ResetTableOpt(PGLOBAL g, bool dox); + virtual void ResetSize(void); + virtual int GetBadLines(void) {return Txfp->GetNerr();} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetProgMax(PGLOBAL g); + virtual int Cardinality(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + + protected: + // Members are inherited from TDBDOS + }; // end of class TDBFIX + +/***********************************************************************/ +/* Class BINCOL: BIN access method column descriptor. */ +/* This A.M. is used for file processed by blocks. */ +/***********************************************************************/ +class DllExport BINCOL : public DOSCOL { + friend class TDBFIX; + public: + // Constructors + BINCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am = "BIN"); + BINCOL(BINCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_BIN;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + + protected: + BINCOL(void) {} // Default constructor not to be used + + // Members + char Fmt; // The column numeric format + }; // end of class BINCOL + +/***********************************************************************/ +/* This is the class declaration for the DBF columns catalog table. */ +/***********************************************************************/ +class TDBDCL : public TDBCAT { + public: + // Constructor + TDBDCL(PDOSDEF tdp) : TDBCAT(tdp) {Fn = tdp->GetFn();} + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g) {return DBFColumns(g, Fn, false);} + + // Members + char *Fn; // The DBF file (path) name + }; // end of class TDBOCL + + +#endif // __TABFIX__ diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp new file mode 100644 index 00000000000..95e99b01b82 --- /dev/null +++ b/storage/connect/tabfmt.cpp @@ -0,0 +1,1433 @@ +/************* TabFmt C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABFMT */ +/* ------------- */ +/* Version 3.8 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2001 - 2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the TABFMT classes DB execution routines. */ +/* The base class CSV is comma separated files. */ +/* FMT (Formatted) files are those having a complex internal record */ +/* format described in the Format keyword of their definition. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#include <locale.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#include "osutil.h" +#else +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#include "osutil.h" +#else +#include <io.h> +#endif +#include <fcntl.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "mycat.h" +#include "filamap.h" +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT +#include "tabfmt.h" +#include "tabmul.h" +#define NO_FUNC +#include "plgcnx.h" // For DB types +#include "resource.h" + +/***********************************************************************/ +/* This should be an option. */ +/***********************************************************************/ +#define MAXCOL 200 /* Default max column nb in result */ +#define TYPE_UNKNOWN 10 /* Must be greater than other types */ + +extern "C" int trace; + +/***********************************************************************/ +/* CSVColumns: constructs the result blocks containing the description */ +/* of all the columns of a CSV file that will be retrieved by #GetData.*/ +/* Note: the algorithm to set the type is based on the internal values */ +/* of types (TYPE_STRING < TYPE_FLOAT < TYPE_INT) (1 < 2 < 7). */ +/* If these values are changed, this will have to be revisited. */ +/***********************************************************************/ +PQRYRES CSVColumns(PGLOBAL g, char *fn, char sep, char q, + int hdr, int mxr, bool info) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, + FLD_PREC, FLD_LENGTH, FLD_SCALE}; + static unsigned int length[] = {6, 6, 8, 10, 10, 6}; + char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096]; + int i, imax, hmax, n, nerr, phase, blank, digit, dec, type; + int ncol = sizeof(dbtype) / sizeof(int); + int num_read = 0, num_max = 10000000; // Statistics + int len[MAXCOL], typ[MAXCOL], prc[MAXCOL]; + FILE *infile; + PQRYRES qrp; + PCOLRES crp; + + if (info) { + imax = 0; + length[0] = 128; + goto skipit; + } // endif info + +// num_max = atoi(p+1); // Max num of record to test +#if defined(WIN32) + if (sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6)) + dechar = '.'; + else + dechar = ','; +#else // !WIN32 + dechar = '.'; +#endif // !WIN32 + + if (trace) + htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n", + SVP(fn), sep, q, hdr, mxr); + + if (!fn) { + strcpy(g->Message, MSG(MISSING_FNAME)); + return NULL; + } // endif fn + + imax = hmax = nerr = 0; + mxr = max(0, mxr); + + for (i = 0; i < MAXCOL; i++) { + colname[i] = NULL; + len[i] = 0; + typ[i] = TYPE_UNKNOWN; + prc[i] = 0; + } // endfor i + + /*********************************************************************/ + /* Open the input file. */ + /*********************************************************************/ + PlugSetPath(filename, fn, PlgGetDataPath(g)); + + if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r"))) + return NULL; + + if (hdr) { + /*******************************************************************/ + /* Make the column names from the first line. */ + /*******************************************************************/ + phase = 0; + + if (fgets(buf, sizeof(buf), infile)) { + n = strlen(buf) + 1; + buf[n - 2] = '\0'; +#if defined(UNIX) + // The file can be imported from Windows + if (buf[n - 3] == '\r') + buf[n - 3] = 0; +#endif // UNIX + p = (char*)PlugSubAlloc(g, NULL, n); + memcpy(p, buf, n); + + //skip leading blanks + for (; *p == ' '; p++) ; + + if (q && *p == q) { + // Header is quoted + p++; + phase = 1; + } // endif q + + colname[0] = p; + } else { + sprintf(g->Message, MSG(FILE_IS_EMPTY), fn); + goto err; + } // endif's + + for (i = 1; *p; p++) + if (phase == 1 && *p == q) { + *p = '\0'; + phase = 0; + } else if (*p == sep && !phase) { + *p = '\0'; + + //skip leading blanks + for (; *(p+1) == ' '; p++) ; + + if (q && *(p+1) == q) { + // Header is quoted + p++; + phase = 1; + } // endif q + + colname[i++] = p + 1; + } // endif sep + + num_read++; + imax = hmax = i; + + for (i = 0; i < hmax; i++) + length[0] = max(length[0], strlen(colname[i])); + + } // endif hdr + + for (num_read++; num_read <= num_max; num_read++) { + /*******************************************************************/ + /* Now start the reading process. Read one line. */ + /*******************************************************************/ + if (fgets(buf, sizeof(buf), infile)) { + n = strlen(buf); + buf[n - 1] = '\0'; +#if defined(UNIX) + // The file can be imported from Windows + if (buf[n - 2] == '\r') + buf[n - 2] = 0; +#endif // UNIX + } else if (feof(infile)) { + sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1); + break; + } else { + sprintf(g->Message, MSG(ERR_READING_REC), num_read, fn); + goto err; + } // endif's + + /*******************************************************************/ + /* Make the test for field lengths. */ + /*******************************************************************/ + i = n = phase = blank = digit = dec = 0; + + for (p = buf; *p; p++) + if (*p == sep) { + if (phase != 1) { + if (i == MAXCOL - 1) { + sprintf(g->Message, MSG(TOO_MANY_FIELDS), num_read, fn); + goto err; + } // endif i + + if (n) { + len[i] = max(len[i], n); + type = (digit || (dec && n == 1)) ? TYPE_STRING + : (dec) ? TYPE_FLOAT : TYPE_INT; + typ[i] = min(type, typ[i]); + prc[i] = max((typ[i] == TYPE_FLOAT) ? (dec - 1) : 0, prc[i]); + } // endif n + + i++; + n = phase = blank = digit = dec = 0; + } else // phase == 1 + n++; + + } else if (*p == ' ') { + if (phase < 2) + n++; + + if (blank) + digit = 1; + + } else if (*p == q) { + if (phase == 0) { + if (blank) + if (++nerr > mxr) { + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + n = 0; + phase = digit = 1; + } else if (phase == 1) { + if (*(p+1) == q) { + // This is currently not implemented for CSV tables +// if (++nerr > mxr) { +// sprintf(g->Message, MSG(QUOTE_IN_QUOTE), num_read); +// goto err; +// } else +// goto skip; + + p++; + n++; + } else + phase = 2; + + } else if (++nerr > mxr) { // phase == 2 + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + } else { + if (phase == 2) + if (++nerr > mxr) { + sprintf(g->Message, MSG(MISPLACED_QUOTE), num_read); + goto err; + } else + goto skip; + + // isdigit cannot be used here because of debug assert + if (!strchr("0123456789", *p)) { + if (!digit && *p == dechar) + dec = 1; // Decimal point found + else if (blank || !(*p == '-' || *p == '+')) + digit = 1; + + } else if (dec) + dec++; // More decimals + + n++; + blank = 1; + } // endif's *p + + if (phase == 1) + if (++nerr > mxr) { + sprintf(g->Message, MSG(UNBALANCE_QUOTE), num_read); + goto err; + } else + goto skip; + + if (n) { + len[i] = max(len[i], n); + type = (digit || n == 0 || (dec && n == 1)) ? TYPE_STRING + : (dec) ? TYPE_FLOAT : TYPE_INT; + typ[i] = min(type, typ[i]); + prc[i] = max((typ[i] == TYPE_FLOAT) ? (dec - 1) : 0, prc[i]); + } // endif n + + imax = max(imax, i+1); + skip: ; // Skip erroneous line + } // endfor num_read + + if (trace) { + htrc("imax=%d Lengths:", imax); + + for (i = 0; i < imax; i++) + htrc(" %d", len[i]); + + htrc("\n"); + } // endif trace + + fclose(infile); + + skipit: + if (trace) + htrc("CSVColumns: imax=%d hmax=%d len=%d\n", + imax, hmax, length[0]); + + /*********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /*********************************************************************/ + qrp = PlgAllocResult(g, ncol, imax, IDS_COLUMNS + 3, + dbtype, buftyp, fldtyp, length, true, false); + qrp->Nblin = imax; + + if (info) + return qrp; + + /*********************************************************************/ + /* Now get the results into blocks. */ + /*********************************************************************/ + for (i = 0; i < imax; i++) { + if (i >= hmax) { + sprintf(buf, "COL%.3d", i+1); + p = buf; + } else + p = colname[i]; + + if (typ[i] == TYPE_UNKNOWN) // Void column + typ[i] = TYPE_STRING; + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(p, i); + crp = crp->Next; // Data Type + crp->Kdata->SetValue(typ[i], i); + crp = crp->Next; // Type Name + crp->Kdata->SetValue(GetTypeName(typ[i]), i); + crp = crp->Next; // Precision + crp->Kdata->SetValue(len[i], i); + crp = crp->Next; // Length + crp->Kdata->SetValue(len[i], i); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue(prc[i], i); + } // endfor i + + /*********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /*********************************************************************/ + return qrp; + + err: + fclose(infile); + return NULL; + } // end of CSVCColumns + +/* --------------------------- Class CSVDEF -------------------------- */ + +/***********************************************************************/ +/* CSVDEF constructor. */ +/***********************************************************************/ +CSVDEF::CSVDEF(void) + { + Fmtd = Accept = Header = false; + Maxerr = 0; + Quoted = -1; + Sep = ','; + Qot = '\0'; + } // end of CSVDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool CSVDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char buf[8]; + + // Double check correctness of offset values + if (Catfunc == FNC_NO) + for (PCOLDEF cdp = To_Cols; cdp; cdp = cdp->GetNext()) + if (cdp->GetOffset() < 1) { + strcpy(g->Message, MSG(BAD_OFFSET_VAL)); + return true; + } // endif Offset + + // Call DOSDEF DefineAM with am=CSV so FMT is not confused with FIX + if (DOSDEF::DefineAM(g, "CSV", poff)) + return true; + + Cat->GetCharCatInfo("Separator", ",", buf, sizeof(buf)); + Sep = (strlen(buf) == 2 && buf[0] == '\\' && buf[1] == 't') ? '\t' : *buf; + Quoted = Cat->GetIntCatInfo("Quoted", -1); + Cat->GetCharCatInfo("Qchar", "", buf, sizeof(buf)); + Qot = *buf; + + if (Qot && Quoted < 0) + Quoted = 0; + else if (!Qot && Quoted >= 0) + Qot = '"'; + + Fmtd = (!Sep || (am && (*am == 'F' || *am == 'f'))); + Header = (Cat->GetIntCatInfo("Header", 0) != 0); + Maxerr = Cat->GetIntCatInfo("Maxerr", 0); + Accept = (Cat->GetIntCatInfo("Accept", 0) != 0); + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) + { + PTDBASE tdbp; + + if (Catfunc != FNC_COL) { + USETEMP tmp = PlgGetUser(g)->UseTemp; + bool map = Mapped && mode != MODE_INSERT && + !(tmp != TMP_NO && mode == MODE_UPDATE) && + !(tmp == TMP_FORCE && + (mode == MODE_UPDATE || mode == MODE_DELETE)); + PTXF txfp; + + /*******************************************************************/ + /* Allocate a file processing class of the proper type. */ + /*******************************************************************/ + if (map) { + // Should be now compatible with UNIX + txfp = new(g) MAPFAM(this); + } else if (Compressed) { +#if defined(ZIP_SUPPORT) + if (Compressed == 1) + txfp = new(g) ZIPFAM(this); + else { + strcpy(g->Message, "Compress 2 not supported yet"); +// txfp = new(g) ZLBFAM(defp); + return NULL; + } // endelse +#else // !ZIP_SUPPORT + strcpy(g->Message, "Compress not supported"); + return NULL; +#endif // !ZIP_SUPPORT + } else + txfp = new(g) DOSFAM(this); + + /*******************************************************************/ + /* Allocate a TDB of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*******************************************************************/ + if (!Fmtd) + tdbp = new(g) TDBCSV(this, txfp); + else + tdbp = new(g) TDBFMT(this, txfp); + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); + + } else + tdbp = new(g)TDBCCL(this); + + return tdbp; + } // end of GetTable + +/* -------------------------- Class TDBCSV --------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBCSV class. */ +/***********************************************************************/ +TDBCSV::TDBCSV(PCSVDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp) + { +#if defined(_DEBUG) + assert (tdp); +#endif + Field = NULL; + Offset = NULL; + Fldlen = NULL; + Fields = 0; + Nerr = 0; + Quoted = tdp->Quoted; + Maxerr = tdp->Maxerr; + Accept = tdp->Accept; + Header = tdp->Header; + Sep = tdp->GetSep(); + Qot = tdp->GetQot(); + } // end of TDBCSV standard constructor + +TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp) + { + Fields = tdbp->Fields; + + if (Fields) { + if (tdbp->Offset) + Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields); + + if (tdbp->Fldlen) + Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields); + + Field = (PSZ *)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields); + + for (int i = 0; i < Fields; i++) { + if (Offset) + Offset[i] = tdbp->Offset[i]; + + if (Fldlen) + Fldlen[i] = tdbp->Fldlen[i]; + + if (Field) { + assert (Fldlen); + Field[i] = (PSZ)PlugSubAlloc(g, NULL, Fldlen[i] + 1); + Field[i][Fldlen[i]] = '\0'; + } // endif Field + + } // endfor i + + } else { + Field = NULL; + Offset = NULL; + Fldlen = NULL; + } // endif Fields + + Nerr = tdbp->Nerr; + Maxerr = tdbp->Maxerr; + Quoted = tdbp->Quoted; + Accept = tdbp->Accept; + Header = tdbp->Header; + Sep = tdbp->Sep; + Qot = tdbp->Qot; + } // end of TDBCSV copy constructor + +// Method +PTDB TDBCSV::CopyOne(PTABS t) + { + PTDB tp; + PCSVCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBCSV(g, this); + + for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) { + cp2 = new(g) CSVCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate CSV column description block. */ +/***********************************************************************/ +PCOL TDBCSV::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) CSVCOL(g, cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* Check whether the number of errors is greater than the maximum. */ +/***********************************************************************/ +bool TDBCSV::CheckErr(void) + { + return (++Nerr) > Maxerr; + } // end of CheckErr + +/***********************************************************************/ +/* CSV EstimatedLength. Returns an estimated minimum line length. */ +/***********************************************************************/ +int TDBCSV::EstimatedLength(PGLOBAL g) + { + if (trace) + htrc("EstimatedLength: Fields=%d Columns=%p\n", Fields, Columns); + + if (!Fields) { + PCSVCOL colp; + + for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) + if (!colp->IsSpecial()) // Not a pseudo column + Fields = max(Fields, (int)colp->Fldnum); + + if (Columns) + Fields++; // Fldnum was 0 based + + } // endif Fields + + return (int)Fields; // Number of separators if all fields are null + } // end of Estimated Length + +#if 0 +/***********************************************************************/ +/* CSV tables favor the use temporary files for Update. */ +/***********************************************************************/ +bool TDBCSV::IsUsingTemp(PGLOBAL g) + { + USETEMP usetemp = PlgGetUser(g)->UseTemp; + + return (usetemp == TMP_YES || usetemp == TMP_FORCE || + (usetemp == TMP_AUTO && Mode == MODE_UPDATE)); + } // end of IsUsingTemp +#endif // 0 (Same as TDBDOS one) + +/***********************************************************************/ +/* CSV Access Method opening routine. */ +/* First allocate the Offset and Fldlen arrays according to the */ +/* greatest field used in that query. Then call the DOS opening fnc. */ +/***********************************************************************/ +bool TDBCSV::OpenDB(PGLOBAL g) + { + bool rc = false; + PCOLDEF cdp; + PDOSDEF tdp = (PDOSDEF)To_Def; + + if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) { + // Allocate the storage used to read (or write) records + int i, len; + PCSVCOL colp; + + if (!Fields) // May have been set in TABFMT::OpenDB + if (Mode != MODE_UPDATE && Mode != MODE_INSERT) { + for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) + if (!colp->IsSpecial()) // Not a pseudo column + Fields = max(Fields, (int)colp->Fldnum); + + if (Columns) + Fields++; // Fldnum was 0 based + + } else + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) + Fields++; + + Offset = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields); + Fldlen = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields); + + if (Mode == MODE_INSERT || Mode == MODE_UPDATE) { + Field = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields); + Fldtyp = (bool*)PlugSubAlloc(g, NULL, sizeof(bool) * Fields); + } // endif Mode + + for (i = 0; i < Fields; i++) { + Offset[i] = 0; + Fldlen[i] = 0; + + if (Field) { + Field[i] = NULL; + Fldtyp[i] = false; + } // endif Field + + } // endfor i + + if (Field) + // Prepare writing fields + if (Mode != MODE_UPDATE) + for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) { + i = colp->Fldnum; + len = colp->GetLength(); + Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1); + Field[i][len] = '\0'; + Fldlen[i] = len; + Fldtyp[i] = IsTypeNum(colp->GetResultType()); + } // endfor colp + + else // MODE_UPDATE + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) { + i = cdp->GetOffset() - 1; + len = cdp->GetLength(); + Field[i] = (PSZ)PlugSubAlloc(g, NULL, len + 1); + Field[i][len] = '\0'; + Fldlen[i] = len; + Fldtyp[i] = IsTypeNum(cdp->GetType()); + } // endfor colp + + } // endif Use + + if (Header) { + // Check that the Lrecl is at least equal to the header line length + int headlen = 0; + PCOLDEF cdp; + PDOSDEF tdp = (PDOSDEF)To_Def; + + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) + headlen += strlen(cdp->GetName()) + 3; // 3 if names are quoted + + if (headlen > Lrecl) { + Lrecl = headlen; + Txfp->Lrecl = headlen; + } // endif headlen + + } // endif Header + + Nerr = 0; + rc = TDBDOS::OpenDB(g); + + if (!rc && Mode == MODE_UPDATE && To_Kindex) + // Because KINDEX::Init is executed in mode READ, we must restore + // the Fldlen array that was modified when reading the table file. + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) + Fldlen[cdp->GetOffset() - 1] = cdp->GetLength(); + + return rc; + } // end of OpenDB + +/***********************************************************************/ +/* SkipHeader: Physically skip first header line if applicable. */ +/* This is called from TDBDOS::OpenDB and must be executed before */ +/* Kindex construction if the file is accessed using an index. */ +/***********************************************************************/ +bool TDBCSV::SkipHeader(PGLOBAL g) + { + int len = GetFileLength(g); + bool rc = false; + +#if defined(_DEBUG) + if (len < 0) + return true; +#endif // _DEBUG + + if (Header) { + if (Mode == MODE_INSERT) { + if (!len) { + // New file, the header line must be constructed and written + int i, n = 0; + int hlen = 0; + bool q = Qot && Quoted > 0; + PCOLDEF cdp; + + // Estimate the length of the header list + for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) { + hlen += (1 + strlen(cdp->GetName())); + hlen += ((q) ? 2 : 0); + n++; // Calculate the number of columns + } // endfor cdp + + if (hlen > Lrecl) { + sprintf(g->Message, MSG(LRECL_TOO_SMALL), hlen); + return true; + } // endif hlen + + // File is empty, write a header record + memset(To_Line, 0, Lrecl); + + // The column order in the file is given by the offset value + for (i = 1; i <= n; i++) + for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) + if (cdp->GetOffset() == i) { + if (q) + To_Line[strlen(To_Line)] = Qot; + + strcat(To_Line, cdp->GetName()); + + if (q) + To_Line[strlen(To_Line)] = Qot; + + if (i < n) + To_Line[strlen(To_Line)] = Sep; + + } // endif Offset + + rc = (Txfp->WriteBuffer(g) == RC_FX); + } // endif !FileLength + + } else if (Mode == MODE_DELETE) { + if (len) + rc = (Txfp->SkipRecord(g, true) == RC_FX); + + } else if (len) // !Insert && !Delete + rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g)); + + } // endif Header + + return rc; + } // end of SkipHeader + +/***********************************************************************/ +/* ReadBuffer: Physical read routine for the CSV access method. */ +/***********************************************************************/ +int TDBCSV::ReadBuffer(PGLOBAL g) + { + char *p1, *p2, *p = NULL; + int i, n, len, rc = Txfp->ReadBuffer(g); + bool bad = false; + + if (trace > 1) + htrc("CSV: Row is '%s' rc=%d\n", To_Line, rc); + + if (rc != RC_OK || !Fields) + return rc; + else + p2 = To_Line; + + // Find the offsets and lengths of the columns for this row + for (i = 0; i < Fields; i++) { + if (!bad) { + if (Qot && *p2 == Qot) { // Quoted field + for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2) + if (*(p + 1) == Qot) + n++; // Doubled internal quotes + else + break; // Final quote + + if (p) { + len = p++ - p2; + +// if (Sep != ' ') +// for (; *p == ' '; p++) ; // Skip blanks + + if (*p != Sep && i != Fields - 1) { // Should be the separator + if (CheckErr()) { + sprintf(g->Message, MSG(MISSING_FIELD), + i+1, Name, RowNumber(g)); + return RC_FX; + } else if (Accept) + bad = true; + else + return RC_NF; + + } // endif p + + if (n) { + int j, k; + + // Suppress the double of internal quotes + for (j = k = 0; j < len; j++, k++) { + if (p2[j] == Qot) + j++; // skip first one + + p2[k] = p2[j]; + } // endfor i, j + + len -= n; + } // endif n + + } else if (CheckErr()) { + sprintf(g->Message, MSG(BAD_QUOTE_FIELD), + Name, i+1, RowNumber(g)); + return RC_FX; + } else if (Accept) { + len = strlen(p2); + bad = true; + } else + return RC_NF; + + } else if ((p = strchr(p2, Sep))) + len = p - p2; + else if (i == Fields - 1) + len = strlen(p2); + else if (Accept && Maxerr == 0) { + len = strlen(p2); + bad = true; + } else if (CheckErr()) { + sprintf(g->Message, MSG(MISSING_FIELD), i+1, Name, RowNumber(g)); + return RC_FX; + } else if (Accept) { + len = strlen(p2); + bad = true; + } else + return RC_NF; + + } else + len = 0; + + Offset[i] = p2 - To_Line; + + if (Mode != MODE_UPDATE) + Fldlen[i] = len; + else if (len > Fldlen[i]) { + sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, RowNumber(g)); + return RC_FX; + } else { + strncpy(Field[i], p2, len); + Field[i][len] = '\0'; + } // endif Mode + + if (p) + p2 = p + 1; + + } // endfor i + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Data Base write routine CSV file access method. */ +/***********************************************************************/ +int TDBCSV::WriteDB(PGLOBAL g) + { + char sep[2], qot[2]; + int i, nlen, oldlen = strlen(To_Line); + + if (trace > 1) + htrc("CSV WriteDB: R%d Mode=%d key=%p link=%p\n", + Tdb_No, Mode, To_Key_Col, To_Link); + + // Before writing the line we must check its length + if ((nlen = CheckWrite(g)) < 0) + return RC_FX; + + // Before writing the line we must make it + sep[0] = Sep; + sep[1] = '\0'; + qot[0] = Qot; + qot[1] = '\0'; + *To_Line = '\0'; + + for (i = 0; i < Fields; i++) { + if (i) + strcat(To_Line, sep); + + if (Field[i]) + if (!strlen(Field[i])) { + // Generally null fields are not quoted + if (Quoted > 2) + // Except if explicitely required + strcat(strcat(To_Line, qot), qot); + + } else if (Qot && (strchr(Field[i], Sep) || *Field[i] == Qot + || Quoted > 1 || (Quoted == 1 && !Fldtyp[i]))) + if (strchr(Field[i], Qot)) { + // Field contains quotes that must be doubled + int j, k = strlen(To_Line), n = strlen(Field[i]); + + To_Line[k++] = Qot; + + for (j = 0; j < n; j++) { + if (Field[i][j] == Qot) + To_Line[k++] = Qot; + + To_Line[k++] = Field[i][j]; + } // endfor j + + To_Line[k++] = Qot; + To_Line[k] = '\0'; + } else + strcat(strcat(strcat(To_Line, qot), Field[i]), qot); + + else + strcat(To_Line, Field[i]); + + } // endfor i + +#if defined(_DEBUG) + assert ((unsigned)nlen == strlen(To_Line)); +#endif + + if (Mode == MODE_UPDATE && nlen < oldlen + && !((PDOSFAM)Txfp)->GetUseTemp()) { + // In Update mode with no temp file, line length must not change + To_Line[nlen] = Sep; + + for (nlen++; nlen < oldlen; nlen++) + To_Line[nlen] = ' '; + + To_Line[nlen] = '\0'; + } // endif + + if (trace > 1) + htrc("Write: line is=%s", To_Line); + + /*********************************************************************/ + /* Now start the writing process. */ + /*********************************************************************/ + return Txfp->WriteBuffer(g); + } // end of WriteDB + +/***********************************************************************/ +/* Check whether a new line fit in the file lrecl size. */ +/***********************************************************************/ +int TDBCSV::CheckWrite(PGLOBAL g) + { + int maxlen, n, nlen = (Fields - 1); + + if (trace > 1) + htrc("CheckWrite: R%d Mode=%d\n", Tdb_No, Mode); + + // Before writing the line we must check its length + maxlen = (Mode == MODE_UPDATE && !Txfp->GetUseTemp()) + ? strlen(To_Line) : Lrecl; + + // Check whether record is too int + for (int i = 0; i < Fields; i++) + if (Field[i]) { + if (!(n = strlen(Field[i]))) + n += (Quoted > 2 ? 2 : 0); + else if (strchr(Field[i], Sep) || (Qot && *Field[i] == Qot) + || Quoted > 1 || (Quoted == 1 && !Fldtyp[i])) + if (!Qot) { + sprintf(g->Message, MSG(SEP_IN_FIELD), i + 1); + return -1; + } else { + // Quotes inside a quoted field must be doubled + char *p1, *p2; + + for (p1 = Field[i]; (p2 = strchr(p1, Qot)); p1 = p2 + 1) + n++; + + n += 2; // Outside quotes + } // endif + + if ((nlen += n) > maxlen) { + strcpy(g->Message, MSG(LINE_TOO_LONG)); + return -1; + } // endif nlen + + } // endif Field + + return nlen; + } // end of CheckWrite + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBFMT class. */ +/***********************************************************************/ +TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp) + { + FldFormat = tdbp->FldFormat; + To_Fld = tdbp->To_Fld; + FmtTest = tdbp->FmtTest; + Linenum = tdbp->Linenum; + } // end of TDBFMT copy constructor + +// Method +PTDB TDBFMT::CopyOne(PTABS t) + { + PTDB tp; + PCSVCOL cp1, cp2; +//PFMTCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBFMT(g, this); + + for (cp1 = (PCSVCOL)Columns; cp1; cp1 = (PCSVCOL)cp1->GetNext()) { +//for (cp1 = (PFMTCOL)Columns; cp1; cp1 = (PFMTCOL)cp1->GetNext()) { + cp2 = new(g) CSVCOL(cp1, tp); // Make a copy +// cp2 = new(g) FMTCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate FMT column description block. */ +/***********************************************************************/ +PCOL TDBFMT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) CSVCOL(g, cdp, this, cprec, n); +//return new(g) FMTCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* FMT EstimatedLength. Returns an estimated minimum line length. */ +/* The big problem here is how can we astimated that minimum ? */ +/***********************************************************************/ +int TDBFMT::EstimatedLength(PGLOBAL g) + { + // This is rather stupid !!! + return ((PDOSDEF)To_Def)->GetEnding() + (int)((Lrecl / 10) + 1); + } // end of EstimatedLength + +/***********************************************************************/ +/* FMT Access Method opening routine. */ +/***********************************************************************/ +bool TDBFMT::OpenDB(PGLOBAL g) + { + Linenum = 0; + + if (Use != USE_OPEN && (Columns || Mode == MODE_UPDATE)) { + // Make the formats used to read records + PSZ pfm; + int i, n; + PCSVCOL colp; + PCOLDEF cdp; + PDOSDEF tdp = (PDOSDEF)To_Def; + +// if (Mode != MODE_UPDATE) { + for (colp = (PCSVCOL)Columns; colp; colp = (PCSVCOL)colp->Next) + if (!colp->IsSpecial()) // Not a pseudo column + Fields = max(Fields, (int)colp->Fldnum); + + if (Columns) + Fields++; // Fldnum was 0 based + +// } else +// for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) +// Fields++; + + To_Fld = PlugSubAlloc(g, NULL, Lrecl + 1); + FldFormat = (PSZ*)PlugSubAlloc(g, NULL, sizeof(PSZ) * Fields); + memset(FldFormat, 0, sizeof(PSZ) * Fields); + FmtTest = (int*)PlugSubAlloc(g, NULL, sizeof(int) * Fields); + memset(FmtTest, 0, sizeof(int) * Fields); + + // Get the column formats + for (cdp = tdp->GetCols(); cdp; cdp = cdp->GetNext()) + if ((i = cdp->GetOffset() - 1) < Fields) { + if (!(pfm = cdp->GetFmt())) { + sprintf(g->Message, MSG(NO_FLD_FORMAT), i + 1, Name); + return true; + } // endif pfm + + // Roughly check the Fmt format + if ((n = strlen(pfm) - 2) < 4) { + sprintf(g->Message, MSG(BAD_FLD_FORMAT), i + 1, Name); + return true; + } // endif n + + FldFormat[i] = (PSZ)PlugSubAlloc(g, NULL, n + 5); + strcpy(FldFormat[i], pfm); + + if (!strcmp(pfm + n, "%m")) { + // This is a field that can be missing. Flag it so it can + // be handled with special processing. + FldFormat[i][n+1] = 'n'; // To have sscanf normal processing + FmtTest[i] = 2; + } else if (i+1 < Fields && strcmp(pfm + n, "%n")) { + // There are trailing characters after the field contents + // add a marker for the next field start position. + strcat(FldFormat[i], "%n"); + FmtTest[i] = 1; + } // endif's + + } // endif i + + } // endif Use + + return TDBCSV::OpenDB(g); + } // end of OpenDB + +/***********************************************************************/ +/* ReadBuffer: Physical read routine for the FMT access method. */ +/***********************************************************************/ +int TDBFMT::ReadBuffer(PGLOBAL g) + { + int i, len, n, deb, fin, nwp, pos = 0, rc; + bool bad = false; + + if ((rc = Txfp->ReadBuffer(g)) != RC_OK || !Fields) + return rc; + else + ++Linenum; + + if (trace > 1) + htrc("FMT: Row %d is '%s' rc=%d\n", Linenum, To_Line, rc); + + // Find the offsets and lengths of the columns for this row + for (i = 0; i < Fields; i++) { + if (!bad) { + deb = fin = -1; + + if (!FldFormat[i]) { + n = 0; + } else if (FmtTest[i] == 1) { + nwp = -1; + n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin, &nwp); + } else { + n = sscanf(To_Line + pos, FldFormat[i], &deb, To_Fld, &fin); + + if (n != 1 && (deb >= 0 || i == Fields - 1) && FmtTest[i] == 2) { + // Missing optional field, not an error + n = 1; + + if (i == Fields - 1) + fin = deb = 0; + else + fin = deb; + + } // endif n + + nwp = fin; + } // endif i + + if (n != 1 || deb < 0 || fin < 0 || nwp < 0) { + // This is to avoid a very strange sscanf bug occuring + // with fields that ends with a null character. + // This bug causes subsequent sscanf to return in error, + // so next lines are not parsed correctly. + sscanf("a", "%*c"); // Seems to reset things Ok + + if (CheckErr()) { + sprintf(g->Message, MSG(BAD_LINEFLD_FMT), Linenum, i + 1, Name); + return RC_FX; + } else if (Accept) + bad = true; + else + return RC_NF; + + } // endif n... + + } // endif !bad + + if (!bad) { + Offset[i] = pos + deb; + len = fin - deb; + } else { + nwp = 0; + Offset[i] = pos; + len = 0; + } // endif bad + +// if (Mode != MODE_UPDATE) + Fldlen[i] = len; +// else if (len > Fldlen[i]) { +// sprintf(g->Message, MSG(FIELD_TOO_LONG), i+1, To_Tdb->RowNumber(g)); +// return RC_FX; +// } else { +// strncpy(Field[i], To_Line + pos, len); +// Field[i][len] = '\0'; +// } // endif Mode + + pos += nwp; + } // endfor i + + if (bad) + Nerr++; + else + sscanf("a", "%*c"); // Seems to reset things Ok + + return rc; + } // end of ReadBuffer + +/***********************************************************************/ +/* Data Base write routine FMT file access method. */ +/***********************************************************************/ +int TDBFMT::WriteDB(PGLOBAL g) + { + sprintf(g->Message, MSG(FMT_WRITE_NIY), "FMT"); + return RC_FX; // NIY + } // end of WriteDB + +// ------------------------ CSVCOL functions ---------------------------- + +/***********************************************************************/ +/* CSVCOL public constructor */ +/***********************************************************************/ +CSVCOL::CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i) + : DOSCOL(g, cdp, tdbp, cprec, i, "CSV") + { + Fldnum = Deplac - 1; + Deplac = 0; + } // end of CSVCOL constructor + +/***********************************************************************/ +/* CSVCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +CSVCOL::CSVCOL(CSVCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp) + { + Fldnum = col1->Fldnum; + } // end of CSVCOL copy constructor + +/***********************************************************************/ +/* VarSize: This function tells UpdateDB whether or not the block */ +/* optimization file must be redone if this column is updated, even */ +/* it is not sorted or clustered. This applies to a blocked table, */ +/* because if it is updated using a temporary file, the block size */ +/* may be modified. */ +/***********************************************************************/ +bool CSVCOL::VarSize(void) + { + PTXF txfp = ((PTDBCSV)To_Tdb)->Txfp; + + if (txfp->IsBlocked() && txfp->GetUseTemp()) + // Blocked table using a temporary file + return true; + else + return false; + + } // end VarSize + +/***********************************************************************/ +/* ReadColumn: call DOSCOL::ReadColumn after having set the offet */ +/* and length of the field to read as calculated by TDBCSV::ReadDB. */ +/***********************************************************************/ +void CSVCOL::ReadColumn(PGLOBAL g) + { + int rc; + PTDBCSV tdbp = (PTDBCSV)To_Tdb; + + /*********************************************************************/ + /* If physical reading of the line was deferred, do it now. */ + /*********************************************************************/ + if (!tdbp->IsRead()) + if ((rc = tdbp->ReadBuffer(g)) != RC_OK) { + if (rc == RC_EF) + sprintf(g->Message, MSG(INV_DEF_READ), rc); + + longjmp(g->jumper[g->jump_level], 34); + } // endif + + if (tdbp->Mode != MODE_UPDATE) { + int colen = Long; // Column length + + // Set the field offset and length for this row + Deplac = tdbp->Offset[Fldnum]; // Field offset + Long = tdbp->Fldlen[Fldnum]; // Field length + + if (trace > 1) + htrc("CSV ReadColumn %s Fldnum=%d offset=%d fldlen=%d\n", + Name, Fldnum, Deplac, Long); + + if (Long > colen && tdbp->CheckErr()) { + Long = colen; // Restore column length + sprintf(g->Message, MSG(FLD_TOO_LNG_FOR), + Fldnum + 1, Name, To_Tdb->RowNumber(g), tdbp->GetFile(g)); + longjmp(g->jumper[g->jump_level], 34); + } // endif Long + + // Now do the reading + DOSCOL::ReadColumn(g); + + // Restore column length + Long = colen; + } else { // Mode Update + // Field have been copied in TDB Field array + PSZ fp = tdbp->Field[Fldnum]; + + Value->SetValue_psz(fp); + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // endif Mode + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: The column is written in TDBCSV matching Field. */ +/***********************************************************************/ +void CSVCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[32]; + int flen; + PTDBCSV tdbp = (PTDBCSV)To_Tdb; + + if (trace > 1) + htrc("CSV WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, tdbp->GetTdb_No(), ColUse, Status); + + flen = GetLength(); + + if (trace > 1) + htrc("Lrecl=%d Long=%d field=%d coltype=%d colval=%p\n", + tdbp->Lrecl, Long, flen, Buf_Type, Value); + + /*********************************************************************/ + /* Check whether the new value has to be converted to Buf_Type. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + /*********************************************************************/ + /* Get the string representation of the column value. */ + /*********************************************************************/ + p = Value->ShowValue(buf); + + if (trace > 1) + htrc("new length(%p)=%d\n", p, strlen(p)); + + if ((signed)strlen(p) > flen) { + sprintf(g->Message, MSG(BAD_FLD_LENGTH), Name, p, flen, + tdbp->RowNumber(g), tdbp->GetFile(g)); + longjmp(g->jumper[g->jump_level], 34); + } // endif + + if (trace > 1) + htrc("buffer=%s\n", p); + + /*********************************************************************/ + /* Updating must be done also during the first pass so writing the */ + /* updated record can be checked for acceptable record length. */ + /*********************************************************************/ + if (Fldnum < 0) { + // This can happen for wrong offset value in XDB files + sprintf(g->Message, MSG(BAD_FIELD_RANK), Fldnum + 1, Name); + longjmp(g->jumper[g->jump_level], 34); + } else + strncpy(tdbp->Field[Fldnum], p, flen); + + if (trace > 1) + htrc(" col written: '%s'\n", p); + + } // end of WriteColumn + +/* ---------------------------TDBCCL class --------------------------- */ + +/***********************************************************************/ +/* TDBCCL class constructor. */ +/***********************************************************************/ +TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp) + { + Fn = tdp->GetFn(); + Hdr = tdp->Header; + Mxr = tdp->Maxerr; + Qtd = tdp->Quoted; + Sep = tdp->Sep; + } // end of TDBCCL constructor + +/***********************************************************************/ +/* GetResult: Get the list the CSV file columns. */ +/***********************************************************************/ +PQRYRES TDBCCL::GetResult(PGLOBAL g) + { + return CSVColumns(g, Fn, Sep, Qtd, Hdr, Mxr, false); + } // end of GetResult + +/* ------------------------ End of TabFmt ---------------------------- */ diff --git a/storage/connect/tabfmt.h b/storage/connect/tabfmt.h new file mode 100644 index 00000000000..05b9d6445c6 --- /dev/null +++ b/storage/connect/tabfmt.h @@ -0,0 +1,190 @@ +/*************** TabFmt H Declares Source Code File (.H) ***************/ +/* Name: TABFMT.H Version 2.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */ +/* */ +/* This file contains the CSV and FMT classes declares. */ +/***********************************************************************/ +#include "xtable.h" // Base class declares +#include "tabdos.h" + +//pedef struct _tabdesc *PTABD; // For friend setting +typedef class TDBFMT *PTDBFMT; + +/***********************************************************************/ +/* Functions used externally. */ +/***********************************************************************/ +PQRYRES CSVColumns(PGLOBAL g, char *fn, char sep, char q, + int hdr, int mxr, bool info); + +/***********************************************************************/ +/* CSV table. */ +/***********************************************************************/ +class DllExport CSVDEF : public DOSDEF { /* Logical table description */ + friend class TDBCSV; + friend class TDBCCL; + public: + // Constructor + CSVDEF(void); + + // Implementation + virtual const char *GetType(void) {return "CSV";} + char GetSep(void) {return Sep;} + char GetQot(void) {return Qot;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE mode); + + protected: + // Members + bool Fmtd; /* true for formatted files */ +//bool Accept; /* true if wrong lines are accepted */ + bool Header; /* true if first line contains headers */ +//int Maxerr; /* Maximum number of bad records */ + int Quoted; /* Quoting level for quoted fields */ + char Sep; /* Separator for standard CSV files */ + char Qot; /* Character for quoted strings */ + }; // end of CSVDEF + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* that are CSV files with columns separated by the Sep character. */ +/***********************************************************************/ +class TDBCSV : public TDBDOS { + friend class CSVCOL; + public: + // Constructor + TDBCSV(PCSVDEF tdp, PTXF txfp); + TDBCSV(PGLOBAL g, PTDBCSV tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_CSV;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBCSV(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); +//virtual bool IsUsingTemp(PGLOBAL g); + virtual int GetBadLines(void) {return (int)Nerr;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual bool OpenDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int CheckWrite(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); // Physical file read + + // Specific routines + virtual int EstimatedLength(PGLOBAL g); + virtual bool SkipHeader(PGLOBAL g); + virtual bool CheckErr(void); + + protected: + // Members + PSZ *Field; // Field to write to current line + int *Offset; // Column offsets for current record + int *Fldlen; // Column field length for current record + bool *Fldtyp; // true for numeric fields + int Fields; // Number of fields to handle + int Nerr; // Number of bad records + int Maxerr; // Maximum number of bad records + int Quoted; // Quoting level for quoted fields + bool Accept; // true if bad lines are accepted + bool Header; // true if first line contains column headers + char Sep; // Separator + char Qot; // Quoting character + }; // end of class TDBCSV + +/***********************************************************************/ +/* Class CSVCOL: CSV access method column descriptor. */ +/* This A.M. is used for Comma Separated V(?) files. */ +/***********************************************************************/ +class CSVCOL : public DOSCOL { + friend class TDBCSV; + friend class TDBFMT; + public: + // Constructors + CSVCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i); + CSVCOL(CSVCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType() {return TYPE_AM_CSV;} + + // Methods + virtual bool VarSize(void); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); +// void Print(FILE *, uint); + + protected: + // Default constructor not to be used + CSVCOL(void) {} + + // Members + int Fldnum; // Field ordinal number (0 based) + }; // end of class CSVCOL + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* whose record format is described by a Format keyword. */ +/***********************************************************************/ +class TDBFMT : public TDBCSV { + friend class CSVCOL; +//friend class FMTCOL; + public: + // Standard constructor + TDBFMT(PCSVDEF tdp, PTXF txfp) : TDBCSV(tdp, txfp) + {FldFormat = NULL; To_Fld = NULL; FmtTest = NULL; Linenum = 0;} + + // Copy constructor + TDBFMT(PGLOBAL g, PTDBFMT tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_FMT;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBFMT(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); +//virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); +//virtual int CheckWrite(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); // Physical file read + + // Specific routines + virtual int EstimatedLength(PGLOBAL g); + + protected: + // Members + PSZ *FldFormat; // Field read format + void *To_Fld; // To field test buffer + int *FmtTest; // Test on ending by %n or %m + int Linenum; // Last read line + }; // end of class TDBFMT + +/***********************************************************************/ +/* This is the class declaration for the CSV catalog table. */ +/***********************************************************************/ +class TDBCCL : public TDBCAT { + public: + // Constructor + TDBCCL(PCSVDEF tdp); + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + // Members + char *Fn; // The CSV file (path) name + bool Hdr; // true if first line contains headers + int Mxr; // Maximum number of bad records + int Qtd; // Quoting level for quoted fields + char Sep; // Separator for standard CSV files + }; // end of class TDBCCL + +/* ------------------------- End of TabFmt.H ------------------------- */ diff --git a/storage/connect/table.cpp b/storage/connect/table.cpp new file mode 100644 index 00000000000..3768109809e --- /dev/null +++ b/storage/connect/table.cpp @@ -0,0 +1,606 @@ +/************** Table C++ Functions Source Code File (.CPP) ************/ +/* Name: TABLE.CPP Version 2.6 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2013 */ +/* */ +/* This file contains the TBX, TDB and OPJOIN classes functions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/* xobject.h is header containing XOBJECT derived classes declares. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "tabcol.h" +#include "filamtxt.h" +#include "tabdos.h" +//#include "catalog.h" +#include "reldef.h" + +int TDB::Tnum = 0; + +extern "C" int trace; // The general trace value + +/***********************************************************************/ +/* Utility routines. */ +/***********************************************************************/ +void NewPointer(PTABS, void *, void *); +void AddPointer(PTABS, void *); + +/* ---------------------------- class TBX ---------------------------- */ + +/***********************************************************************/ +/* TBX public constructors. */ +/***********************************************************************/ +TBX::TBX(void) + { + Use = USE_NO; + To_Orig = NULL; + To_Filter = NULL; + } // end of TBX constructor + +TBX::TBX(PTBX txp) + { + Use = txp->Use; + To_Orig = txp; + To_Filter = NULL; + } // end of TBX copy constructor + +// Methods + +/* ---------------------------- class TDB ---------------------------- */ + +/***********************************************************************/ +/* TDB public constructors. */ +/***********************************************************************/ +TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum) + { + Next = NULL; + Name = (tdp) ? tdp->GetName() : NULL; + To_Table = NULL; + Columns = NULL; + Degree = (tdp) ? tdp->GetDegree() : 0; + Mode = MODE_READ; + } // end of TDB standard constructor + +TDB::TDB(PTDB tdbp) : TBX(tdbp), Tdb_No(++Tnum) + { + Next = NULL; + Name = tdbp->Name; + To_Table = tdbp->To_Table; + Columns = NULL; + Degree = tdbp->Degree; + Mode = tdbp->Mode; + } // end of TDB copy constructor + +/***********************************************************************/ +/* OpenTable: Call AM open routine. */ +/***********************************************************************/ +bool TDB::OpenTable(PGLOBAL g, PSQL sqlp, MODE mode) + { + if (trace) + htrc("Open Tdb_No=%d use=%d type=%d tdb.Mode=%d mode=%d\n", + Tdb_No, Use, GetAmType(), Mode, mode); + + switch (Use) { + case USE_LIN: + /*****************************************************************/ + /* If table is read/only, only MODE_READ is allowed. */ + /*****************************************************************/ + if (IsReadOnly() && mode != MODE_READ) { + strcpy(g->Message, MSG(READ_ONLY)); + return true; + } // endif ReadOnly + + /*****************************************************************/ + /* This could be done in any order. */ + /* Note: for not Read only first table in open in that mode. */ + /*****************************************************************/ + if (Next) + Next->OpenTable(g, sqlp, MODE_READ); + + Mode = mode; + + /*****************************************************************/ + /* Pre-opening is done, allocate select buffers now. */ + /*****************************************************************/ + Use = USE_READY; + break; + + case USE_READY: + /*****************************************************************/ + /* This is to open files in reverse order. */ + /*****************************************************************/ + if (Next) + if (Next->OpenTable(g, sqlp, mode)) + return true; + + /*****************************************************************/ + /* This was moved after filter conversion so filtering can be */ + /* done when making index tables for DOS files. */ + /* Also it was moved after allocating select buffers so some */ + /* data can be pre-read during open to allow storage sorting. */ + /*****************************************************************/ + if (OpenDB(g)) // Do open the table file + return true; + + Use = USE_OPEN; + break; + + case USE_OPEN: + /*****************************************************************/ + /* Table is already open. */ + /* Call open routine that will just "rewind" the files. */ + /*****************************************************************/ + if (OpenDB(g)) // Rewind the table file + return true; + + break; + + default: + sprintf(g->Message, MSG(TDB_USE_ERROR), Use); + return true; + } // endswitch Use + + return false; + } // end of OpenTable + +/***********************************************************************/ +/* CloseTable: Close a table of any AM type. */ +/***********************************************************************/ +void TDB::CloseTable(PGLOBAL g) + { + if (trace) + htrc("CloseTable: tdb_no %d use=%d amtype=%d am.Mode=%d\n", + Tdb_No, Use, GetAmType(), Mode); + + CloseDB(g); + Use = USE_READY; // x'7FFD' + Mode = MODE_ANY; + } // end of CloseTable + +// Methods + +/***********************************************************************/ +/* RowNumber: returns the current row ordinal number. */ +/***********************************************************************/ +int TDB::RowNumber(PGLOBAL g, bool b) + { + sprintf(g->Message, MSG(ROWID_NOT_IMPL), GetAmName(g, GetAmType())); + return 0; + } // end of RowNumber + +PTBX TDB::Copy(PTABS t) + { + PTDB tp, tdb1, tdb2 = NULL, outp = NULL; +//PGLOBAL g = t->G; // Is this really useful ??? + + for (tdb1 = this; tdb1; tdb1 = tdb1->Next) { + tp = tdb1->CopyOne(t); + + if (!outp) + outp = tp; + else + tdb2->Next = tp; + + tdb2 = tp; + NewPointer(t, tdb1, tdb2); + } // endfor tdb1 + + return outp; + } // end of Copy + +void TDB::Print(PGLOBAL g, FILE *f, uint n) + { + PCOL cp; + char m[64]; + + memset(m, ' ', n); // Make margin string + m[n] = '\0'; + + for (PTDB tp = this; tp; tp = tp->Next) { + fprintf(f, "%sTDB (%p) %s no=%d use=%d type=%d\n", m, + tp, tp->Name, tp->Tdb_No, tp->Use, tp->GetAmType()); + + tp->PrintAM(f, m); + fprintf(f, "%s Columns (deg=%d):\n", m, tp->Degree); + + for (cp = tp->Columns; cp; cp = cp->GetNext()) + cp->Print(g, f, n); + + } /* endfor tp */ + + } // end of Print + +void TDB::Print(PGLOBAL g, char *ps, uint z) + { + sprintf(ps, "R%d.%s", Tdb_No, Name); + } // end of Print + +/* -------------------------- class TDBASE --------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBASE class. This is the base class to all */ +/* classes for tables that can be joined together. */ +/***********************************************************************/ +TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp) + { + To_Def = tdp; + To_Link = NULL; + To_Key_Col = NULL; + To_Kindex = NULL; + To_SetCols = NULL; + MaxSize = -1; + Knum = 0; + Read_Only = (tdp) ? tdp->IsReadOnly() : false; + m_data_charset= (tdp) ? tdp->data_charset() : NULL; + } // end of TDBASE constructor + +TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp) + { + To_Def = tdbp->To_Def; + To_SetCols = tdbp->To_SetCols; // ??? + MaxSize = tdbp->MaxSize; + Read_Only = tdbp->Read_Only; + m_data_charset= tdbp->m_data_charset; + } // end of TDBASE copy constructor + +/***********************************************************************/ +/* Return the pointer on the DB catalog this table belongs to. */ +/***********************************************************************/ +PCATLG TDBASE::GetCat(void) + { + return (To_Def) ? To_Def->GetCat() : NULL; + } // end of GetCat + +/***********************************************************************/ +/* Return the datapath of the DB this table belongs to. */ +/***********************************************************************/ +PSZ TDBASE::GetPath(void) + { + return To_Def->GetPath(); + } // end of GetPath + +/***********************************************************************/ +/* Initialize TDBASE based column description block construction. */ +/* name is used to call columns by name. */ +/* num is used by TBL to construct columns by index number. */ +/* Note: name=Null and num=0 for constructing all columns (select *) */ +/***********************************************************************/ +PCOL TDBASE::ColDB(PGLOBAL g, PSZ name, int num) + { + int i; + PCOLDEF cdp; + PCOL cp, colp = NULL, cprec = NULL; + + if (trace) + htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n", + GetAmType(), SVP(name), Name, num); + + for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if ((!name && !num) || + (name && !stricmp(cdp->GetName(), name)) || num == i) { + /*****************************************************************/ + /* Check for existence of desired column. */ + /* Also find where to insert the new block. */ + /*****************************************************************/ + for (cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetIndex() < i) + cprec = cp; + else if (cp->GetIndex() == i) + break; + + if (trace) + htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp); + + /*****************************************************************/ + /* Now take care of Column Description Block. */ + /*****************************************************************/ + if (cp) + colp = cp; + else + colp = MakeCol(g, cdp, cprec, i); + + if (trace) + htrc("colp=%p\n", colp); + + if (name || num) + break; + else if (colp) + cprec = colp; + + } // endif Name + + return (colp); + } // end of ColDB + +/***********************************************************************/ +/* InsertSpecialColumn: Put a special column ahead of the column list.*/ +/***********************************************************************/ +PCOL TDBASE::InsertSpecialColumn(PGLOBAL g, PCOL colp) + { + if (!colp->IsSpecial()) + return NULL; + + colp->SetNext(Columns); + Columns = colp; + return colp; + } // end of InsertSpecialColumn + +/***********************************************************************/ +/* Make a special COLBLK to insert in a table. */ +/***********************************************************************/ +PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLUMN cp) + { + char *name = (char*)cp->GetName(); + PCOL colp; + + if (!strcmp(name, "FILEID")) { +// !strcmp(name, "SERVID")) { + if (!To_Def || !(To_Def->GetPseudo() & 2)) { + sprintf(g->Message, MSG(BAD_SPEC_COLUMN)); + return NULL; + } // endif Pseudo + +// if (!strcmp(name, "FILEID")) + colp = new(g) FIDBLK(cp); +// else +// colp = new(g) SIDBLK(cp); + + } else if (!strcmp(name, "TABID")) { + colp = new(g) TIDBLK(cp); +//} else if (!strcmp(name, "CONID")) { +// colp = new(g) CIDBLK(cp); + } else if (!strcmp(name, "ROWID")) { + colp = new(g) RIDBLK(cp, false); + } else if (!strcmp(name, "ROWNUM")) { + colp = new(g) RIDBLK(cp, true); + } else { + sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); + return NULL; + } // endif's name + + if (!(colp = InsertSpecialColumn(g, colp))) { + sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); + return NULL; + } // endif Insert + + return (colp); + } // end of InsertSpcBlk + +/***********************************************************************/ +/* ResetTableOpt: Wrong for this table type. */ +/***********************************************************************/ +int TDBASE::ResetTableOpt(PGLOBAL g, bool dox) +{ + strcpy(g->Message, "This table is not indexable"); + return RC_INFO; +} // end of ResetTableOpt + +/***********************************************************************/ +/* SetKindex: set or reset the index pointer. */ +/***********************************************************************/ +void TDBASE::SetKindex(PKXBASE kxp) + { + if (To_Kindex) + To_Kindex->Close(); // Discard old index + + To_Kindex = kxp; + } // end of SetKindex + +/***********************************************************************/ +/* SetRecpos: Replace the table at the specified position. */ +/***********************************************************************/ +bool TDBASE::SetRecpos(PGLOBAL g, int recpos) + { + strcpy(g->Message, MSG(SETRECPOS_NIY)); + return true; + } // end of SetRecpos + +/***********************************************************************/ +/* Methods */ +/***********************************************************************/ +void TDBASE::PrintAM(FILE *f, char *m) + { + fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); + } // end of PrintAM + +/***********************************************************************/ +/* Marks DOS/MAP table columns used in internal joins. */ +/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */ +/* points to the currently marked tdb. */ +/* Two questions here: exact meaning of U_J_INT ? */ +/* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */ +/***********************************************************************/ +void TDBASE::MarkDB(PGLOBAL g, PTDB tdb2) + { + if (trace) + htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2); + + } // end of MarkDB + +/* ---------------------------TDBCAT class --------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBCAT class. */ +/***********************************************************************/ +TDBCAT::TDBCAT(PTABDEF tdp) : TDBASE(tdp) + { + Qrp = NULL; + Init = false; + N = -1; + } // end of TDBCAT constructor + +/***********************************************************************/ +/* Allocate CAT column description block. */ +/***********************************************************************/ +PCOL TDBCAT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PCATCOL colp; + + colp = (PCATCOL)new(g) CATCOL(cdp, this, n); + + if (cprec) { + colp->SetNext(cprec->GetNext()); + cprec->SetNext(colp); + } else { + colp->SetNext(Columns); + Columns = colp; + } // endif cprec + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* Initialize: Get the result query block. */ +/***********************************************************************/ +bool TDBCAT::Initialize(PGLOBAL g) + { + if (Init) + return false; + + if (!(Qrp = GetResult(g))) + return true; + + Init = true; + return false; + } // end of Initialize + +/***********************************************************************/ +/* CAT: Get the number of properties. */ +/***********************************************************************/ +int TDBCAT::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + if (Initialize(g)) + return -1; + + MaxSize = Qrp->Nblin; + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* CAT Access Method opening routine. */ +/***********************************************************************/ +bool TDBCAT::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open. */ + /*******************************************************************/ + N = -1; + return false; + } // endif use + + if (Mode != MODE_READ) { + /*******************************************************************/ + /* ODBC Info tables cannot be modified. */ + /*******************************************************************/ + strcpy(g->Message, "CAT tables are read only"); + return true; + } // endif Mode + + /*********************************************************************/ + /* Initialize the ODBC processing. */ + /*********************************************************************/ + if (Initialize(g)) + return true; + + return InitCol(g); + } // end of OpenDB + +/***********************************************************************/ +/* Initialize columns. */ +/***********************************************************************/ +bool TDBCAT::InitCol(PGLOBAL g) + { + PCATCOL colp; + PCOLRES crp; + + for (colp = (PCATCOL)Columns; colp; colp = (PCATCOL)colp->GetNext()) { + for (crp = Qrp->Colresp; crp; crp = crp->Next) + if ((colp->Flag && colp->Flag == crp->Fld) || + (!colp->Flag && !stricmp(colp->Name, crp->Name))) { + colp->Crp = crp; + break; + } // endif Flag + + + if (!colp->Crp /*&& !colp->GetValue()->IsConstant()*/) { + sprintf(g->Message, "Invalid flag %d for column %s", + colp->Flag, colp->Name); + return true; + } // endif Crp + + } // endfor colp + + return false; + } // end of InitCol + +/***********************************************************************/ +/* Data Base read routine for CAT access method. */ +/***********************************************************************/ +int TDBCAT::ReadDB(PGLOBAL g) + { + return (++N < Qrp->Nblin) ? RC_OK : RC_EF; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for CAT access methods. */ +/***********************************************************************/ +int TDBCAT::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "CAT tables are read only"); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for CAT access methods. */ +/***********************************************************************/ +int TDBCAT::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, "Delete not enabled for CAT tables"); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for WMI access method. */ +/***********************************************************************/ +void TDBCAT::CloseDB(PGLOBAL g) + { + // Nothing to do + } // end of CloseDB + +// ------------------------ CATCOL functions ---------------------------- + +/***********************************************************************/ +/* CATCOL public constructor. */ +/***********************************************************************/ +CATCOL::CATCOL(PCOLDEF cdp, PTDB tdbp, int n) + : COLBLK(cdp, tdbp, n) + { + Tdbp = (PTDBCAT)tdbp; + Crp = NULL; + Flag = cdp->GetOffset(); + } // end of WMICOL constructor + +/***********************************************************************/ +/* Read the next Data Source elements. */ +/***********************************************************************/ +void CATCOL::ReadColumn(PGLOBAL g) + { + // Get the value of the Name or Description property + Value->SetValue_pvblk(Crp->Kdata, Tdbp->N); + } // end of ReadColumn + diff --git a/storage/connect/tabmac.cpp b/storage/connect/tabmac.cpp new file mode 100644 index 00000000000..f072465ced5 --- /dev/null +++ b/storage/connect/tabmac.cpp @@ -0,0 +1,458 @@ +/***********************************************************************/ +/* TABMAC: Author Olivier Bertrand -- PlugDB -- 2008-2012 */ +/* From the article and sample code by Khalid Shaikh. */ +/* TABMAC: virtual table to get the list of MAC addresses. */ +/***********************************************************************/ +#if defined(WIN32) +#include "my_global.h" +//#include <iphlpapi.h> +#else // !WIN32 +#error This is a WIN32 only table type +#endif // !WIN32 +#include "global.h" +#include "plgdbsem.h" +//#include "catalog.h" +#include "reldef.h" +#include "xtable.h" +#include "colblk.h" +#include "tabmac.h" + +#if 0 // This is placed here just to know what are the actual values +#define MAX_ADAPTER_DESCRIPTION_LENGTH 128 +#define MAX_ADAPTER_NAME_LENGTH 256 +#define MAX_ADAPTER_ADDRESS_LENGTH 8 +#define DEFAULT_MINIMUM_ENTITIES 32 +#define MAX_HOSTNAME_LEN 128 +#define MAX_DOMAIN_NAME_LEN 128 +#define MAX_SCOPE_ID_LEN 256 + +#define BROADCAST_NODETYPE 1 +#define PEER_TO_PEER_NODETYPE 2 +#define MIXED_NODETYPE 4 +#define HYBRID_NODETYPE 8 + +#define IP_ADAPTER_DDNS_ENABLED 0x01 +#define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02 +#define IP_ADAPTER_DHCP_ENABLED 0x04 +#define IP_ADAPTER_RECEIVE_ONLY 0x08 +#define IP_ADAPTER_NO_MULTICAST 0x10 +#define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20 +#endif // 0 + +/* -------------- Implementation of the MAC classes ------------------ */ + +/***********************************************************************/ +/* DefineAM: define specific AM block values from MAC file. */ +/***********************************************************************/ +bool MACDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB MACDEF::GetTable(PGLOBAL g, MODE m) + { + return new(g) TDBMAC(this); + } // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBMAC class. */ +/***********************************************************************/ +TDBMAC::TDBMAC(PMACDEF tdp) : TDBASE(tdp) + { + FixedInfo = NULL; + Piaf = NULL; + Curp = NULL; + Next = NULL; + Buflen = 0; + Fix = false; + Adap = false; + N = 0; + } // end of TDBMAC constructor + +/***********************************************************************/ +/* Allocate MAC column description block. */ +/***********************************************************************/ +PCOL TDBMAC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PCOL colp; + + colp = new(g) MACCOL(cdp, this, n); + + if (cprec) { + colp->SetNext(cprec->GetNext()); + cprec->SetNext(colp); + } else { + colp->SetNext(Columns); + Columns = colp; + } // endif cprec + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* MAC: Get the number of found adapters. */ +/***********************************************************************/ +void TDBMAC::MakeErrorMsg(PGLOBAL g, DWORD drc) + { + if (drc == ERROR_BUFFER_OVERFLOW) + sprintf(g->Message, + "GetAdaptersInfo: Buffer Overflow buflen=%d maxsize=%d", + Buflen, MaxSize); + else if (drc == ERROR_INVALID_PARAMETER) + strcpy(g->Message, "GetAdaptersInfo: Invalid parameters"); + else if (drc == ERROR_NO_DATA) + strcpy(g->Message, + "No adapter information exists for the local computer"); + else if (drc == ERROR_NOT_SUPPORTED) + strcpy(g->Message, "GetAdaptersInfo is not supported"); + else + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + 0, g->Message, sizeof(g->Message), NULL); + + } // end of MakeErrorMsg + +/***********************************************************************/ +/* GetMacInfo: Get info for all found adapters. */ +/***********************************************************************/ +bool TDBMAC::GetMacInfo(PGLOBAL g) + { + DWORD drc; + + if (GetMaxSize(g) < 0) + return true; + else if (MaxSize == 0) + return false; + + Piaf = (PIP_ADAPTER_INFO)PlugSubAlloc(g, NULL, Buflen); + drc = GetAdaptersInfo(Piaf, &Buflen); + + if (drc == ERROR_SUCCESS) { + Next = Piaf; // Next is the first one + return false; // Success + } // endif drc + + MakeErrorMsg(g, drc); + return true; + } // end of GetMacInfo + +/***********************************************************************/ +/* GetFixedInfo: Get info for network parameters. */ +/***********************************************************************/ +bool TDBMAC::GetFixedInfo(PGLOBAL g) + { + ULONG len; + DWORD drc; + + FixedInfo = (FIXED_INFO*)PlugSubAlloc(g, NULL, sizeof(FIXED_INFO)); + len = sizeof(FIXED_INFO); + drc = GetNetworkParams(FixedInfo, &len); + + if (drc == ERROR_BUFFER_OVERFLOW) { + FixedInfo = (FIXED_INFO*)PlugSubAlloc(g, NULL, len); + drc = GetNetworkParams(FixedInfo, &len); + } // endif drc + + if (drc != ERROR_SUCCESS) { + sprintf(g->Message, "GetNetworkParams failed. Rc=%08x\n", drc); + return true; + } // endif drc + + return false; + } // end of GetFixedInfo + +/***********************************************************************/ +/* MAC: Get the number of found adapters. */ +/***********************************************************************/ +int TDBMAC::GetMaxSize(PGLOBAL g) + { + if (Use != USE_OPEN) + // Called from info, Adap and Fix are not set yet + return 1; + + if (MaxSize < 0) { + // Best method + if (Adap) { + DWORD drc = GetAdaptersInfo(NULL, &(Buflen = 0)); + + if (drc == ERROR_SUCCESS) + MaxSize = (Fix) ? 1 : 0; + else if (drc == ERROR_BUFFER_OVERFLOW) { + // sizeof(IP_ADAPTER_INFO) was returning 640 but is now sometimes + // returning 648 while the Buflen setting remains the same (n*640) + // >> Of course, the code above contains a race condition.... + // if the size of the structure Windows wants to return grows after + // the first call to GetAdaptersInfo() but before the second call + // to GetAdaptersInfo(), the second call to GetAdaptersInfo() will + // fail with ERROR_BUFFER_OVERFLOW as well, and your function won't + // work (by Jeremy Friesner on stackoverflow.com). + // That's why we add something to it to be comfortable. + MaxSize = (Buflen + 600) / sizeof(IP_ADAPTER_INFO); + + // Now Buflen must be updated if 648 is true. + Buflen = MaxSize * sizeof(IP_ADAPTER_INFO); + } else + MakeErrorMsg(g, drc); + + } else + MaxSize = (Fix) ? 1 : 0; + +#if 0 + // This method returns too many adapters + DWORD dw, drc = GetNumberOfInterfaces((PDWORD)&dw); + + if (drc == NO_ERROR) { + MaxSize = (int)dw; + Buflen = MaxSize * sizeof(IP_ADAPTER_INFO); + } else + MakeErrorMsg(g, 0); +#endif + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* MAC Access Method opening routine. */ +/***********************************************************************/ +bool TDBMAC::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, this should not happen. */ + /*******************************************************************/ + strcpy(g->Message, "TDBMAC should not be reopened"); + return true; + } // endif use + + if (Mode != MODE_READ) { + /*******************************************************************/ + /* MAC tables cannot be modified. */ + /*******************************************************************/ + strcpy(g->Message, "MAC tables are read only"); + return true; + } else + Use = USE_OPEN; + + /*********************************************************************/ + /* Get the adapters info. */ + /*********************************************************************/ + if (Adap && GetMacInfo(g)) + return true; + + if (Fix && GetFixedInfo(g)) + return true; + + /*********************************************************************/ + /* All is done. */ + /*********************************************************************/ + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for MAC access method. */ +/***********************************************************************/ +int TDBMAC::ReadDB(PGLOBAL g) + { + Curp = Next; + + if (Curp) + Next = Curp->Next; + else if (N || !Fix) + return RC_EF; + + N++; + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for MAC access methods. */ +/***********************************************************************/ +int TDBMAC::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "MAC tables are read only"); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for MAC access methods. */ +/***********************************************************************/ +int TDBMAC::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, "Delete not enabled for MAC tables"); + return RC_FX; + } // end of DeleteDB + +// ------------------------ MACCOL functions ---------------------------- + +/***********************************************************************/ +/* MACCOL public constructor. */ +/***********************************************************************/ +MACCOL::MACCOL(PCOLDEF cdp, PTDB tdbp, int n) + : COLBLK(cdp, tdbp, n) + { + Tdbp = (PTDBMAC)tdbp; + Flag = cdp->GetOffset(); + + if (Flag < 10) + Tdbp->Fix = true; + else + Tdbp->Adap = true; + + } // end of MACCOL constructor + +/***********************************************************************/ +/* Read the next MAC address elements. */ +/***********************************************************************/ +void MACCOL::ReadColumn(PGLOBAL g) + { + // Type conversion is handled by Value set routines + char *p = NULL, buf[260] = ""; + unsigned int i; + int n = 0; + PIP_ADAPTER_INFO adp = Tdbp->Curp; + FIXED_INFO *fip = Tdbp->FixedInfo; + + if (!adp && Flag >= 10) { + // Fix info row, no adapter info available + switch (Flag) { + case 13: + case 14: + case 19: + case 22: + case 23: + n = 0; + break; + default: + p = ""; + } // endswitch Flag + + } else switch (Flag) { + // FIXED INFO + case 1: // Host Name + p = fip->HostName; + break; + case 2: // Domain Name + p = fip->DomainName; + break; + case 3: // DNS IPaddress + p = (fip->CurrentDnsServer) + ? (char*)&fip->CurrentDnsServer->IpAddress + : (char*)&fip->DnsServerList.IpAddress; + break; + case 4: // Node Type + n = (int)fip->NodeType; + break; + case 5: // Scope ID ??? + p = fip->ScopeId; + break; + case 6: // Routing enabled + n = (int)fip->EnableRouting; + break; + case 7: // Proxy enabled + n = (int)fip->EnableProxy; + break; + case 8: // DNS enabled + n = (int)fip->EnableDns; + break; + // ADAPTERS INFO + case 10: // Name + p = adp->AdapterName; + break; + case 11: // Description + if ((p = strstr(adp->Description, " - Packet Scheduler Miniport"))) { + strncpy(buf, adp->Description, p - adp->Description); + i = p - adp->Description; + strncpy(buf, adp->Description, i); + buf[i] = 0; + p = buf; + } else if ((p = strstr(adp->Description, + " - Miniport d'ordonnancement de paquets"))) { + i = p - adp->Description; + strncpy(buf, adp->Description, i); + buf[i] = 0; + p = buf; + } else + p = adp->Description; + + break; + case 12: // MAC Address + for (p = buf, i = 0; i < adp->AddressLength; i++) { + if (i) + strcat(p++, "-"); + + p += sprintf(p, "%.2X", adp->Address[i]); + } // endfor i + + p = buf; + break; + case 13: // Type +#if 0 // This is not found in the SDK + switch (adp->Type) { + case IF_ETHERNET_ADAPTERTYPE: p = "Ethernet Adapter"; break; + case IF_TOKEN_RING_ADAPTERTYPE: p = "Token Ring Adapter"; break; + case IF_FDDI_ADAPTERTYPE: p = "FDDI Adapter"; break; + case IF_PPP_ADAPTERTYPE: p = "PPP Adapter"; break; + case IF_LOOPBACK_ADAPTERTYPE: p = "Loop Back Adapter"; break; +// case IF_SLIP_ADAPTERTYPE: p = "Generic Slip Adapter"; break; + default: + sprintf(buf, "Other Adapter, type=%d", adp->Type); + p = buf; + } // endswitch Type +#endif // 0 + n = (int)adp->Type; + break; + case 14: // DHCP enabled + n = (int)adp->DhcpEnabled; + break; + case 15: // IP Address + p = (adp->CurrentIpAddress) + ? (char*)&adp->CurrentIpAddress->IpAddress + : (char*)&adp->IpAddressList.IpAddress; + break; + case 16: // Subnet Mask + p = (adp->CurrentIpAddress) + ? (char*)&adp->CurrentIpAddress->IpMask + : (char*)&adp->IpAddressList.IpMask; + break; + case 17: // Gateway + p = (char*)&adp->GatewayList.IpAddress; + break; + case 18: // DHCP Server + p = (char*)&adp->DhcpServer.IpAddress; + break; + case 19: // Have WINS + n = (adp->HaveWins) ? 1 : 0; + break; + case 20: // Primary WINS + p = (char*)&adp->PrimaryWinsServer.IpAddress; + break; + case 21: // Secondary WINS + p = (char*)&adp->SecondaryWinsServer.IpAddress; + break; + case 22: // Lease obtained + n = (int)adp->LeaseObtained; + break; + case 23: // Lease expires + n = (int)adp->LeaseExpires; + break; + default: + if (Buf_Type == TYPE_STRING) { + sprintf(buf, "Invalid flag value %d", Flag); + p = buf; + } else + n = 0; + + } // endswitch Flag + + if (p) + Value->SetValue_psz(p); + else + Value->SetValue(n); + + } // end of ReadColumn diff --git a/storage/connect/tabmac.h b/storage/connect/tabmac.h new file mode 100644 index 00000000000..cfdf842cdc8 --- /dev/null +++ b/storage/connect/tabmac.h @@ -0,0 +1,107 @@ +// TABMAC.H Olivier Bertrand 2011-2012 +// MAC: virtual table to Get Mac Addresses via GetAdaptersInfo +#if defined(WIN32) +#include <windows.h> +#include <iphlpapi.h> +#else // !WIN32 +#error This is a WIN32 only table TYPE +#endif // !WIN32 + +/***********************************************************************/ +/* Definitions. */ +/***********************************************************************/ +typedef class MACDEF *PMACDEF; +typedef class TDBMAC *PTDBMAC; +typedef class MACCOL *PMACCOL; + +/* -------------------------- MAC classes ---------------------------- */ + +/***********************************************************************/ +/* MAC: virtual table to get the list of MAC addresses. */ +/***********************************************************************/ +class DllExport MACDEF : public TABDEF { /* Logical table description */ + friend class TDBMAC; + public: + // Constructor + MACDEF(void) {Pseudo = 3;} + + // Implementation + virtual const char *GetType(void) {return "MAC";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + virtual bool DeleteTableFile(PGLOBAL g) {return true;} + + protected: + // Members + }; // end of MACDEF + +/***********************************************************************/ +/* This is the class declaration for the MAC table. */ +/***********************************************************************/ +class TDBMAC : public TDBASE { + friend class MACCOL; + public: + // Constructor + TDBMAC(PMACDEF tdp); +//TDBMAC(PGLOBAL g, PTDBMAC tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_MAC;} +//virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMAC(g, this);} + + // Methods +//virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void) {return N;} + virtual int RowNumber(PGLOBAL g, bool b = false) {return N;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g) {} + + protected: + // Specific routines + bool GetMacInfo(PGLOBAL g); + bool GetFixedInfo(PGLOBAL g); + void MakeErrorMsg(PGLOBAL g, DWORD drc); + + // Members + FIXED_INFO *FixedInfo; // Points to fixed info structure + PIP_ADAPTER_INFO Piaf; // Points on Adapter info array + PIP_ADAPTER_INFO Curp; // Points on current Adapt info + PIP_ADAPTER_INFO Next; // Points on next Adapt info + ULONG Buflen; // Buffer length + bool Fix; // true if FixedInfo is needed + bool Adap; // true if Piaf is needed + int N; // Row number + }; // end of class TDBMAC + +/***********************************************************************/ +/* Class MACCOL: MAC Address column. */ +/***********************************************************************/ +class MACCOL : public COLBLK { + friend class TDBMAC; + public: + // Constructors + MACCOL(PCOLDEF cdp, PTDB tdbp, int n); +//MACCOL(MACCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_MAC;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + protected: + MACCOL(void) {} // Default constructor not to be used + + // Members + PTDBMAC Tdbp; // Points to MAC table block + int Flag; // Indicates what to display + }; // end of class MACCOL diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp new file mode 100644 index 00000000000..5fe18ef5b1d --- /dev/null +++ b/storage/connect/tabmul.cpp @@ -0,0 +1,1477 @@ +/************* TabMul C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABMUL */ +/* ------------- */ +/* Version 1.7 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to PlugDB Software Development 2003 - 2012 */ +/* Author: Olivier BERTRAND */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the TDBMUL class DB routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* TABMUL.CPP - Source code */ +/* PLGDBSEM.H - DB application declaration file */ +/* TABDOS.H - TABDOS classes declaration file */ +/* TABMUL.H - TABFIX classes declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant section of system dependant header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <stdlib.h> +#include <stdio.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#else +#if defined(UNIX) +#include <fnmatch.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "osutil.h" +#else +//#include <io.h> +#endif +//#include <fcntl.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/***********************************************************************/ +#include "global.h" // global declarations +#include "plgdbsem.h" // DB application declarations +#include "reldef.h" // DB definition declares +#include "filamtxt.h" +#include "tabdos.h" // TDBDOS and DOSCOL class dcls +#include "tabmul.h" // TDBMUL and MULCOL classes dcls + +/* ------------------------- Class TDBMUL ---------------------------- */ + +/***********************************************************************/ +/* TABMUL constructors. */ +/***********************************************************************/ +TDBMUL::TDBMUL(PTDBASE tdbp) : TDBASE(tdbp->GetDef()) + { + Tdbp = tdbp; + Filenames = NULL; + Rows = 0; + Mul = tdbp->GetDef()->GetMultiple(); + NumFiles = 0; + iFile = 0; + } // end of TDBMUL standard constructor + +TDBMUL::TDBMUL(PTDBMUL tdbp) : TDBASE(tdbp) + { + Tdbp = tdbp->Tdbp; + Filenames = tdbp->Filenames; + Rows = tdbp->Rows; + Mul = tdbp->Mul; + NumFiles = tdbp->NumFiles; + iFile = tdbp->iFile; + } // end of TDBMUL copy constructor + +// Method +PTDB TDBMUL::CopyOne(PTABS t) + { + PTDBMUL tp; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBMUL(this); + tp->Tdbp = (PTDBASE)Tdbp->CopyOne(t); + tp->Columns = tp->Tdbp->GetColumns(); + return tp; + } // end of CopyOne + +PTDB TDBMUL::Duplicate(PGLOBAL g) + { + PTDBMUL tmup = new(g) TDBMUL(this); + + tmup->Tdbp = (PTDBASE)Tdbp->Duplicate(g); + return tmup; + } // end of Duplicate + +/***********************************************************************/ +/* Initializes the table filename list. */ +/* Note: tables created by concatenating the file components without */ +/* specifying the LRECL value (that should be restricted to _MAX_PATH)*/ +/* have a LRECL that is the sum of the lengths of all components. */ +/* This is why we use a big filename array to take care of that. */ +/***********************************************************************/ +bool TDBMUL::InitFileNames(PGLOBAL g) + { +#define PFNZ 8192 + char *pfn[PFNZ], filename[_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT]; + int rc, n = 0; + + // The sub table may need to refer to the Table original block + Tdbp->SetTable(To_Table); // Was not set at construction + + PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath()); + + if (Mul == 1) { + /*******************************************************************/ + /* To_File is a multiple name with special characters */ + /*******************************************************************/ +#if defined(WIN32) + char drive[_MAX_DRIVE], direc[_MAX_DIR]; + WIN32_FIND_DATA FileData; + HANDLE hSearch; + + _splitpath(filename, drive, direc, NULL, NULL); + + // Start searching files in the target directory. + hSearch = FindFirstFile(filename, &FileData); + + if (hSearch == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + + if (rc != ERROR_FILE_NOT_FOUND) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, + (LPTSTR)&filename, sizeof(filename), NULL); + sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename); + return true; + } // endif rc + + goto suite; + } // endif hSearch + + while (n < PFNZ) { + strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName); + pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy(pfn[n++], filename); + + if (!FindNextFile(hSearch, &FileData)) { + rc = GetLastError(); + + if (rc != ERROR_NO_MORE_FILES) { + sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc); + FindClose(hSearch); + return true; + } // endif rc + + break; + } // endif FindNextFile + + } // endwhile n + + // Close the search handle. + if (!FindClose(hSearch)) { + strcpy(g->Message, MSG(SRCH_CLOSE_ERR)); + return true; + } // endif FindClose + +#else // !WIN32 + struct stat fileinfo; + char fn[PATH_MAX], direc[PATH_MAX], pattern[256], ftype[8]; + DIR *dir; + struct dirent *entry; + + _splitpath(filename, NULL, direc, pattern, ftype); + strcat(pattern, ftype); + + // Start searching files in the target directory. + if (!(dir = opendir(direc))) { + sprintf(g->Message, MSG(BAD_DIRECTORY), direc, strerror(errno)); + return true; + } // endif dir + + while ((entry = readdir(dir)) && n < PFNZ) { + strcat(strcpy(fn, direc), entry->d_name); + + if (lstat(fn, &fileinfo) < 0) { + sprintf(g->Message, "%s: %s", fn, strerror(errno)); + return true; + } else if (!S_ISREG(fileinfo.st_mode)) + continue; // Not a regular file (should test for links) + + /*******************************************************************/ + /* Test whether the file name matches the table name filter. */ + /*******************************************************************/ + if (fnmatch(pattern, entry->d_name, 0)) + continue; // Not a match + + strcat(strcpy(filename, direc), entry->d_name); + pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy(pfn[n++], filename); + } // endwhile readdir + + // Close the dir handle. + closedir(dir); +#endif // !WIN32 + + } else { + /*******************************************************************/ + /* To_File is the name of a file containing the file name list */ + /*******************************************************************/ + char *p; + FILE *stream; + + if (!(stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "r"))) + return true; + + while (n < PFNZ) { + if (!fgets(filename, sizeof(filename), stream)) { + fclose(stream); + break; + } // endif fgets + + p = filename + strlen(filename) - 1; + +#if defined(UNIX) + // Data files can be imported from Windows (having CRLF) + if (*p == '\n' || *p == '\r') { + // is this enough for Unix ??? + *p--; // Eliminate ending CR or LF character + + if (p >= filename) + // is this enough for Unix ??? + if (*p == '\n' || *p == '\r') + *p--; // Eliminate ending CR or LF character + + } // endif p + +#else + if (*p == '\n') + p--; // Eliminate ending new-line character +#endif + // Trim rightmost blanks + for (; p >= filename && *p == ' '; p--) ; + + *(++p) = '\0'; + + // Suballocate the file name + pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1); + strcpy(pfn[n++], filename); + } // endfor n + + } // endif Mul + +#if defined(WIN32) + suite: +#endif + + if (n) { + Filenames = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*)); + + for (int i = 0; i < n; i++) + Filenames[i] = pfn[i]; + + } else { + Filenames = (char**)PlugSubAlloc(g, NULL, sizeof(char*)); + Filenames[0] = NULL; + } // endif n + + NumFiles = n; + return false; + } // end of InitFileNames + +/***********************************************************************/ +/* The table column list is the sub-table column list. */ +/***********************************************************************/ +PCOL TDBMUL::ColDB(PGLOBAL g, PSZ name, int num) + { + PCOL cp; + + /*********************************************************************/ + /* Because special columns are directly added to the MUL block, */ + /* make sure that the sub-table has the same column list, before */ + /* and after the call to the ColDB function. */ + /*********************************************************************/ + Tdbp->SetColumns(Columns); + cp = Tdbp->ColDB(g, name, num); + Columns = Tdbp->GetColumns(); + return cp; +} // end of ColDB + +/***********************************************************************/ +/* MUL GetProgMax: get the max value for progress information. */ +/***********************************************************************/ +int TDBMUL::GetProgMax(PGLOBAL g) + { + if (!Filenames && InitFileNames(g)) + return -1; + + return NumFiles; // This is a temporary setting + } // end of GetProgMax + +/***********************************************************************/ +/* MUL GetProgCur: get the current value for progress information. */ +/***********************************************************************/ +int TDBMUL::GetProgCur(void) + { + return iFile; // This is a temporary setting + } // end of GetProgMax + +/***********************************************************************/ +/* MUL Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/* Can be used on Multiple FIX table only. */ +/***********************************************************************/ +int TDBMUL::Cardinality(PGLOBAL g) + { + if (!g) + return Tdbp->Cardinality(g); + + if (!Filenames && InitFileNames(g)) + return -1; + + int n, card = 0; + + for (int i = 0; i < NumFiles; i++) { + Tdbp->SetFile(g, Filenames[i]); + Tdbp->ResetSize(); + + if ((n = Tdbp->Cardinality(g)) < 0) { +// strcpy(g->Message, MSG(BAD_CARDINALITY)); + return -1; + } // endif n + + card += n; + } // endfor i + + return card; + } // end of Cardinality + +/***********************************************************************/ +/* Sum up the sizes of all sub-tables. */ +/***********************************************************************/ +int TDBMUL::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + int i; + int mxsz; + + if (!Filenames && InitFileNames(g)) + return -1; + + if (Use == USE_OPEN) { + strcpy(g->Message, MSG(MAXSIZE_ERROR)); + return -1; + } else + MaxSize = 0; + + for (i = 0; i < NumFiles; i++) { + Tdbp->SetFile(g, Filenames[i]); + Tdbp->ResetSize(); + + if ((mxsz = Tdbp->GetMaxSize(g)) < 0) { + MaxSize = -1; + return mxsz; + } // endif mxsz + + MaxSize += mxsz; + } // endfor i + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void TDBMUL::ResetDB(void) + { + for (PCOL colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_FILID) + colp->COLBLK::Reset(); + + Tdbp->ResetDB(); + } // end of ResetDB + +/***********************************************************************/ +/* Returns RowId if b is false or Rownum if b is true. */ +/***********************************************************************/ +int TDBMUL::RowNumber(PGLOBAL g, bool b) + { + return ((b) ? 0 : Rows) + + ((iFile < NumFiles) ? Tdbp->RowNumber(g, b) : 1); + } // end of RowNumber + +/***********************************************************************/ +/* MUL Access Method opening routine. */ +/* Open first file, other will be opened sequencially when reading. */ +/***********************************************************************/ +bool TDBMUL::OpenDB(PGLOBAL g) + { +#ifdef DEBTRACE + htrc("MUL OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n", + this, Tdb_No, Use, To_Key_Col, Mode); +#endif + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, replace it at its beginning. */ + /*******************************************************************/ + if (Filenames[iFile = 0]) { + Tdbp->CloseDB(g); + Tdbp->SetUse(USE_READY); + Tdbp->SetFile(g, Filenames[iFile = 0]); + Tdbp->ResetSize(); + Rows = 0; + ResetDB(); + return Tdbp->OpenDB(g); // Re-open with new file name + } else + return false; + + } // endif use + + /*********************************************************************/ + /* We need to calculate MaxSize before opening the query. */ + /*********************************************************************/ + if (GetMaxSize(g) < 0) + return true; + + /*********************************************************************/ + /* Open the first table file of the list. */ + /*********************************************************************/ +//if (!Filenames && InitFileNames(g)) done in GetMaxSize +// return true; + + if (Filenames[iFile = 0]) { + Tdbp->SetFile(g, Filenames[0]); + Tdbp->SetMode(Mode); + Tdbp->ResetDB(); + Tdbp->ResetSize(); + + if (Tdbp->OpenDB(g)) + return true; + + } // endif *Filenames + + Use = USE_OPEN; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for MUL access method. */ +/***********************************************************************/ +int TDBMUL::ReadDB(PGLOBAL g) + { + int rc; + + if (NumFiles == 0) + return RC_EF; + else if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + strcpy(g->Message, MSG(NO_INDEX_READ)); + rc = RC_FX; + } else { + /*******************************************************************/ + /* Now start the reading process. */ + /*******************************************************************/ + retry: + rc = Tdbp->ReadDB(g); + + if (rc == RC_EF) { + if (Tdbp->GetDef()->GetPseudo() & 1) + // Total number of rows met so far + Rows += Tdbp->RowNumber(g) - 1; + + if (++iFile < NumFiles) { + /***************************************************************/ + /* Continue reading from next table file. */ + /***************************************************************/ + Tdbp->CloseDB(g); + Tdbp->SetUse(USE_READY); + Tdbp->SetFile(g, Filenames[iFile]); + Tdbp->ResetSize(); + ResetDB(); + + if (Tdbp->OpenDB(g)) // Re-open with new file name + return RC_FX; + + goto retry; + } // endif iFile + + } else if (rc == RC_FX) + strcat(strcat(strcat(g->Message, " ("), Tdbp->GetFile(g)), ")"); + + } // endif To_Kindex + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base write routine for MUL access method. */ +/***********************************************************************/ +int TDBMUL::WriteDB(PGLOBAL g) + { + return Tdbp->WriteDB(g); +// strcpy(g->Message, MSG(TABMUL_READONLY)); +// return RC_FX; // NIY + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for MUL access method. */ +/***********************************************************************/ +int TDBMUL::DeleteDB(PGLOBAL g, int irc) + { + // When implementing DELETE_MODE InitFileNames must be updated to + // eliminate CRLF under Windows if the file is read in binary. + strcpy(g->Message, MSG(TABMUL_READONLY)); + return RC_FX; // NIY + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for MUL access method. */ +/***********************************************************************/ +void TDBMUL::CloseDB(PGLOBAL g) + { + if (NumFiles > 0) { + Tdbp->CloseDB(g); + iFile = NumFiles; + } // endif NumFiles + + } // end of CloseDB + +/* --------------------------- Class DIRDEF -------------------------- */ + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool DIRDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + Desc = Fn = Cat->GetStringCatInfo(g, "Filename", NULL); + Incl = (Cat->GetIntCatInfo("Subdir", 0) != 0); + Huge = (Cat->GetIntCatInfo("Huge", 0) != 0); + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB DIRDEF::GetTable(PGLOBAL g, MODE m) + { +#if 0 + if (Huge) + return new(g) TDBDHR(this); // Not implemented yet + else +#endif + if (Incl) + return new(g) TDBSDR(this); // Including sub-directory files + else + return new(g) TDBDIR(this); // Not Including sub-directory files + + } // end of GetTable + +/* ------------------------- Class TDBDIR ---------------------------- */ + +/***********************************************************************/ +/* TABDIR constructors. */ +/***********************************************************************/ +TDBDIR::TDBDIR(PDIRDEF tdp) : TDBASE(tdp) + { + To_File = tdp->Fn; + iFile = 0; +#if defined(WIN32) + memset(&FileData, 0, sizeof(_finddata_t)); + Hsearch = -1; + *Drive = '\0'; +#else // !WIN32 + memset(&Fileinfo, 0, sizeof(struct stat)); + Entry = NULL; + Dir = NULL; + Done = false; + *Pattern = '\0'; +#endif // !WIN32 + *Fpath = '\0'; + *Direc = '\0'; + *Fname = '\0'; + *Ftype = '\0'; + } // end of TDBDIR standard constructor + +TDBDIR::TDBDIR(PTDBDIR tdbp) : TDBASE(tdbp) + { + To_File = tdbp->To_File; + iFile = tdbp->iFile; +#if defined(WIN32) + FileData = tdbp->FileData; + Hsearch = tdbp->Hsearch; + strcpy(Drive, tdbp->Drive); +#else // !WIN32 + Fileinfo = tdbp->Fileinfo; + Entry = tdbp->Entry; + Dir = tdbp->Dir; + Done = tdbp->Done; + strcpy(Pattern, tdbp->Pattern); +#endif // !WIN32 + strcpy(Direc, tdbp->Direc); + strcpy(Fname, tdbp->Fname); + strcpy(Ftype, tdbp->Ftype); + } // end of TDBDIR copy constructor + +// Method +PTDB TDBDIR::CopyOne(PTABS t) + { + PTDB tp; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBDIR(this); + tp->SetColumns(Columns); + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Initialize/get the components of the search file pattern. */ +/***********************************************************************/ +char* TDBDIR::Path(PGLOBAL g) + { + PCATLG cat = PlgGetCatalog(g); + +#if defined(WIN32) + if (!*Drive) { + PlugSetPath(Fpath, To_File, cat->GetDataPath()); + _splitpath(Fpath, Drive, Direc, Fname, Ftype); + } else + _makepath(Fpath, Drive, Direc, Fname, Ftype); // Usefull ??? + + return Fpath; +#else // !WIN32 + if (!Done) { + PlugSetPath(Fpath, To_File, cat->GetDataPath()); + _splitpath(Fpath, NULL, Direc, Fname, Ftype); + strcat(strcpy(Pattern, Fname), Ftype); + Done = true; + } // endif Done + + return Pattern; +#endif // !WIN32 + } // end of Path + +/***********************************************************************/ +/* Allocate DIR column description block. */ +/***********************************************************************/ +PCOL TDBDIR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) DIRCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* DIR GetMaxSize: returns the number of retrieved files. */ +/***********************************************************************/ +int TDBDIR::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + int n = -1; +#if defined(WIN32) + int h; + + // Start searching files in the target directory. + h = _findfirst(Path(g), &FileData); + + if (h != -1) { + for (n = 1;; n++) + if (_findnext(h, &FileData)) + break; + + // Close the search handle. + _findclose(h); + } else + n = 0; + +#else // !WIN32 + Path(g); + + // Start searching files in the target directory. + if (!(Dir = opendir(Direc))) { + sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno)); + return -1; + } // endif dir + + while ((Entry = readdir(Dir))) { + strcat(strcpy(Fpath, Direc), Entry->d_name); + + if (lstat(Fpath, &Fileinfo) < 0) { + sprintf(g->Message, "%s: %s", Fpath, strerror(errno)); + return -1; + } else if (S_ISREG(Fileinfo.st_mode)) + // Test whether the file name matches the table name filter + if (!fnmatch(Pattern, Entry->d_name, 0)) + n++; // We have a match + + } // endwhile Entry + + // Close the DIR handle. + closedir(Dir); +#endif // !WIN32 + MaxSize = n; + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* DIR Access Method opening routine. */ +/* Open first file, other will be opened sequencially when reading. */ +/***********************************************************************/ +bool TDBDIR::OpenDB(PGLOBAL g) + { +#ifdef DEBTRACE + htrc("DIR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n", + this, Tdb_No, Use, Mode); +#endif + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, reopen it. */ + /*******************************************************************/ + CloseDB(g); + SetUse(USE_READY); + } // endif use + + Use = USE_OPEN; +#if !defined(WIN32) + Path(g); // Be sure it is done + Dir = NULL; // For ReadDB +#endif // !WIN32 + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for DIR access method. */ +/***********************************************************************/ +int TDBDIR::ReadDB(PGLOBAL g) + { + int rc = RC_OK; + +#if defined(WIN32) + if (Hsearch == -1) { + /*******************************************************************/ + /* Start searching files in the target directory. The use of the */ + /* Path function is required when called from TDBSDR. */ + /*******************************************************************/ + Hsearch = _findfirst(Path(g), &FileData); + + if (Hsearch == -1) + rc = RC_EF; + else + iFile++; + + } else { + if (_findnext(Hsearch, &FileData)) { + // Restore file name and type pattern + _splitpath(To_File, NULL, NULL, Fname, Ftype); + rc = RC_EF; + } else + iFile++; + + } // endif Hsearch + + if (rc == RC_OK) + _splitpath(FileData.name, NULL, NULL, Fname, Ftype); + +#else // !Win32 + rc = RC_NF; + + if (!Dir) + // Start searching files in the target directory. + if (!(Dir = opendir(Direc))) { + sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno)); + rc = RC_FX; + } // endif dir + + while (rc == RC_NF) + if ((Entry = readdir(Dir))) { + // We need the Fileinfo structure to get info about the file + strcat(strcpy(Fpath, Direc), Entry->d_name); + + if (lstat(Fpath, &Fileinfo) < 0) { + sprintf(g->Message, "%s: %s", Fpath, strerror(errno)); + rc = RC_FX; + } else if (S_ISREG(Fileinfo.st_mode)) + // Test whether the file name matches the table name filter + if (!fnmatch(Pattern, Entry->d_name, 0)) { + iFile++; // We have a match + _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype); + rc = RC_OK; + } // endif fnmatch + + } else { + // Restore file name and type pattern + _splitpath(To_File, NULL, NULL, Fname, Ftype); + rc = RC_EF; + } // endif Entry + +#endif // !WIN32 + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base write routine for DIR access method. */ +/***********************************************************************/ +int TDBDIR::WriteDB(PGLOBAL g) + { + strcpy(g->Message, MSG(TABDIR_READONLY)); + return RC_FX; // NIY + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for DIR access method. */ +/***********************************************************************/ +int TDBDIR::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(TABDIR_READONLY)); + return RC_FX; // NIY + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for MUL access method. */ +/***********************************************************************/ +void TDBDIR::CloseDB(PGLOBAL g) + { +#if defined(WIN32) + // Close the search handle. + _findclose(Hsearch); + Hsearch = -1; +#else // !WIN32 + // Close the DIR handle. + closedir(Dir); + Dir = NULL; +#endif // !WIN32 + iFile = 0; + } // end of CloseDB + +// ------------------------ DIRCOL functions ---------------------------- + +/***********************************************************************/ +/* DIRCOL public constructor. */ +/***********************************************************************/ +DIRCOL::DIRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional DIR access method information for column. + N = cdp->GetOffset(); + } // end of DIRCOL constructor + +/***********************************************************************/ +/* DIRCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +DIRCOL::DIRCOL(DIRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + N = col1->N; + } // end of DIRCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the information */ +/* corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void DIRCOL::ReadColumn(PGLOBAL g) + { + PTDBDIR tdbp = (PTDBDIR)To_Tdb; + +#ifdef DEBTRACE + fprintf(debug, + "DIR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n", + Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N); +#endif + + /*********************************************************************/ + /* Retrieve the information corresponding to the column number. */ + /*********************************************************************/ + switch (N) { +#if defined(WIN32) + case 0: Value->SetValue_psz(tdbp->Drive); break; +#endif // WIN32 + case 1: Value->SetValue_psz(tdbp->Direc); break; + case 2: Value->SetValue_psz(tdbp->Fname); break; + case 3: Value->SetValue_psz(tdbp->Ftype); break; +#if defined(WIN32) + case 4: Value->SetValue((int)tdbp->FileData.attrib); break; + case 5: Value->SetValue((int)tdbp->FileData.size); break; + case 6: Value->SetValue((int)tdbp->FileData.time_write); break; + case 7: Value->SetValue((int)tdbp->FileData.time_create); break; + case 8: Value->SetValue((int)tdbp->FileData.time_access); break; +#else // !WIN32 + case 4: Value->SetValue((int)tdbp->Fileinfo.st_mode); break; + case 5: Value->SetValue((int)tdbp->Fileinfo.st_size); break; + case 6: Value->SetValue((int)tdbp->Fileinfo.st_mtime); break; + case 7: Value->SetValue((int)tdbp->Fileinfo.st_ctime); break; + case 8: Value->SetValue((int)tdbp->Fileinfo.st_atime); break; + case 9: Value->SetValue((int)tdbp->Fileinfo.st_uid); break; + case 10: Value->SetValue((int)tdbp->Fileinfo.st_gid); break; +#endif // !WIN32 + default: + sprintf(g->Message, MSG(INV_DIRCOL_OFST), N); + longjmp(g->jumper[g->jump_level], GetAmType()); + } // endswitch N + + } // end of ReadColumn + +/* ------------------------- Class TDBSDR ---------------------------- */ + +/***********************************************************************/ +/* TABSDR copy constructors. */ +/***********************************************************************/ +TDBSDR::TDBSDR(PTDBSDR tdbp) : TDBDIR(tdbp) + { + Sub = tdbp->Sub; + } // end of TDBSDR copy constructor + +// Method +PTDB TDBSDR::CopyOne(PTABS t) + { + PTDB tp; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBSDR(this); + tp->SetColumns(Columns); + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* SDR GetMaxSize: returns the number of retrieved files. */ +/***********************************************************************/ +int TDBSDR::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + Path(g); + MaxSize = FindInDir(g); + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* SDR GetMaxSize: returns the number of retrieved files. */ +/***********************************************************************/ +int TDBSDR::FindInDir(PGLOBAL g) + { + int n = 0; + size_t m = strlen(Direc); + + // Start searching files in the target directory. +#if defined(WIN32) + int h = _findfirst(Path(g), &FileData); + + if (h != -1) { + for (n = 1;; n++) + if (_findnext(h, &FileData)) + break; + + // Close the search handle. + _findclose(h); + } // endif h + + // Now search files in sub-directories. + _makepath(Fpath, Drive, Direc, "*", ""); + h = _findfirst(Fpath, &FileData); + + if (h != -1) { + while (true) { + if (FileData.attrib & _A_SUBDIR && *FileData.name != '.') { + // Look in the name sub-directory + strcat(strcat(Direc, FileData.name), "\\"); + n += FindInDir(g); + Direc[m] = '\0'; // Restore path + } // endif SUBDIR + + if (_findnext(h, &FileData)) + break; + + } // endwhile + + // Close the search handle. + _findclose(h); + } // endif h +#else // !WIN32 + int k; + DIR *dir = opendir(Direc); + + if (!dir) { + sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno)); + return -1; + } // endif dir + + while ((Entry = readdir(dir))) { + strcat(strcpy(Fpath, Direc), Entry->d_name); + + if (lstat(Fpath, &Fileinfo) < 0) { + sprintf(g->Message, "%s: %s", Fpath, strerror(errno)); + return -1; + } else if (S_ISDIR(Fileinfo.st_mode) && *Entry->d_name != '.') { + // Look in the name sub-directory + strcat(strcat(Direc, Entry->d_name), "/"); + + if ((k = FindInDir(g)) < 0) + return k; + else + n += k; + + Direc[m] = '\0'; // Restore path + } else if (S_ISREG(Fileinfo.st_mode)) + // Test whether the file name matches the table name filter + if (!fnmatch(Pattern, Entry->d_name, 0)) + n++; // We have a match + + } // endwhile readdir + + // Close the DIR handle. + closedir(dir); +#endif // !WIN32 + + return n; + } // end of FindInDir + +/***********************************************************************/ +/* DIR Access Method opening routine. */ +/* Open first file, other will be opened sequencially when reading. */ +/***********************************************************************/ +bool TDBSDR::OpenDB(PGLOBAL g) + { + if (!Sub) { + Path(g); + Sub = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR)); + Sub->Next = NULL; + Sub->Prev = NULL; +#if defined(WIN32) + Sub->H = -1; + Sub->Len = strlen(Direc); +#else // !WIN32 + Sub->D = NULL; + Sub->Len = 0; +#endif // !WIN32 + } // endif To_Sub + + return TDBDIR::OpenDB(g); + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for SDR access method. */ +/***********************************************************************/ +int TDBSDR::ReadDB(PGLOBAL g) + { + int rc; + +#if defined(WIN32) + again: + rc = TDBDIR::ReadDB(g); + + if (rc == RC_EF) { + // Are there more files in sub-directories + retry: + do { + if (Sub->H == -1) { + _makepath(Fpath, Drive, Direc, "*", ""); + Sub->H = _findfirst(Fpath, &FileData); + } else if (_findnext(Sub->H, &FileData)) { + _findclose(Sub->H); + Sub->H = -1; + *FileData.name = '\0'; + } // endif findnext + + } while(*FileData.name == '.'); + + if (Sub->H == -1) { + // No more sub-directories. Are we in a sub-directory? + if (!Sub->Prev) + return rc; // No, all is finished + + // here we must continue in the parent directory + Sub = Sub->Prev; + goto retry; + } else { + // Search next sub-directory + Direc[Sub->Len] = '\0'; + + if (!Sub->Next) { + PSUBDIR sup; + + sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR)); + sup->Next = NULL; + sup->Prev = Sub; + sup->H = -1; + Sub->Next = sup; + } // endif Next + + Sub = Sub->Next; + strcat(strcat(Direc, FileData.name), "\\"); + Sub->Len = strlen(Direc); + + // Reset Hsearch used by TDBDIR::ReadDB + _findclose(Hsearch); + Hsearch = -1; + goto again; + } // endif H + + } // endif rc +#else // !WIN32 + rc = RC_NF; + + again: + if (!Sub->D) + // Start searching files in the target directory. + if (!(Sub->D = opendir(Direc))) { + sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno)); + rc = RC_FX; + } // endif dir + + while (rc == RC_NF) + if ((Entry = readdir(Sub->D))) { + // We need the Fileinfo structure to get info about the file + strcat(strcpy(Fpath, Direc), Entry->d_name); + + if (lstat(Fpath, &Fileinfo) < 0) { + sprintf(g->Message, "%s: %s", Fpath, strerror(errno)); + rc = RC_FX; + } else if (S_ISDIR(Fileinfo.st_mode) && *Entry->d_name != '.') { + // Look in the name sub-directory + if (!Sub->Next) { + PSUBDIR sup; + + sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR)); + sup->Next = NULL; + sup->Prev = Sub; + Sub->Next = sup; + } // endif Next + + Sub = Sub->Next; + Sub->D = NULL; + Sub->Len = strlen(Direc); + strcat(strcat(Direc, Entry->d_name), "/"); + goto again; + } else if (S_ISREG(Fileinfo.st_mode)) + // Test whether the file name matches the table name filter + if (!fnmatch(Pattern, Entry->d_name, 0)) { + iFile++; // We have a match + _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype); + rc = RC_OK; + } // endif fnmatch + + } else { + // No more files. Close the DIR handle. + closedir(Sub->D); + + // Are we in a sub-directory? + if (Sub->Prev) { + // Yes, we must continue in the parent directory + Direc[Sub->Len] = '\0'; + Sub = Sub->Prev; + } else + rc = RC_EF; // No, all is finished + + } // endif Entry + +#endif // !WIN32 + + return rc; + } // end of ReadDB + +#if 0 +/* ------------------------- Class TDBDHR ---------------------------- */ + +/***********************************************************************/ +/* TABDHR constructors. */ +/***********************************************************************/ +TDBDHR::TDBDHR(PDHRDEF tdp) : TDBASE(tdp) + { + memset(&FileData, 0, sizeof(WIN32_FIND_DATA)); + Hsearch = INVALID_HANDLE_VALUE; + iFile = 0; + *Drive = '\0'; + *Direc = '\0'; + *Fname = '\0'; + *Ftype = '\0'; + } // end of TDBDHR standard constructor + +TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp) + { + FileData = tdbp->FileData; + Hsearch = tdbp->Hsearch; + iFile = tdbp->iFile; + strcpy(Drive, tdbp->Drive); + strcpy(Direc, tdbp->Direc); + strcpy(Fname, tdbp->Fname); + strcpy(Ftype, tdbp->Ftype); + } // end of TDBDHR copy constructor + +// Method +PTDB TDBDHR::CopyOne(PTABS t) + { + PTDB tp; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBDHR(this); + tp->Columns = Columns; + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate DHR column description block. */ +/***********************************************************************/ +PCOL TDBDHR::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) DHRCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* DHR GetMaxSize: returns the number of retrieved files. */ +/***********************************************************************/ +int TDBDHR::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + char filename[_MAX_PATH]; + int i, rc; + int n = -1; + HANDLE h; + PDBUSER dup = PlgGetUser(g); + + PlugSetPath(filename, To_File, dup->Path); + + // Start searching files in the target directory. + h = FindFirstFile(filename, &FileData); + + if (h == INVALID_HANDLE_VALUE) { + switch (rc = GetLastError()) { + case ERROR_NO_MORE_FILES: + case ERROR_FILE_NOT_FOUND: + n = 0; + break; + default: + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, rc, 0, + (LPTSTR)&filename, sizeof(filename), NULL); + sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename); + } // endswitch rc + + } else { + for (n = 1;; n++) + if (!FindNextFile(h, &FileData)) { + rc = GetLastError(); + + if (rc != ERROR_NO_MORE_FILES) { + sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc); + n = -1; + } // endif rc + + break; + } // endif FindNextFile + + // Close the search handle. + if (!FindClose(h) && n != -1) + strcpy(g->Message, MSG(SRCH_CLOSE_ERR)); + + } // endif Hsearch + + MaxSize = n; + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* DHR Access Method opening routine. */ +/* Open first file, other will be opened sequencially when reading. */ +/***********************************************************************/ +bool TDBDHR::OpenDB(PGLOBAL g) + { +#ifdef DEBTRACE + htrc("DHR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n", + this, Tdb_No, Use, Mode); +#endif + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, reopen it. */ + /*******************************************************************/ + CloseDB(g); + SetUse(USE_READY); + } // endif use + + /*********************************************************************/ + /* Direct access needed for join or sorting. */ + /*********************************************************************/ + if (NeedIndexing(g)) { + // Direct access of DHR tables is not implemented yet + sprintf(g->Message, MSG(NO_DIR_INDX_RD), "DHR"); + return true; + } // endif NeedIndexing + + Use = USE_OPEN; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for DHR access method. */ +/***********************************************************************/ +int TDBDHR::ReadDB(PGLOBAL g) + { + int rc = RC_OK; + DWORD erc; + + if (Hsearch == INVALID_HANDLE_VALUE) { + char *filename[_MAX_PATH]; + PDBUSER dup = PlgGetUser(g); + + PlugSetPath(filename, To_File, dup->Path); + _splitpath(filename, Drive, Direc, NULL, NULL); + + /*******************************************************************/ + /* Start searching files in the target directory. */ + /*******************************************************************/ + Hsearch = FindFirstFile(filename, &FileData); + + if (Hsearch != INVALID_HANDLE_VALUE) + iFile = 1; + else switch (erc = GetLastError()) { + case ERROR_NO_MORE_FILES: + case ERROR_FILE_NOT_FOUND: +// case ERROR_PATH_NOT_FOUND: ??????? + rc = RC_EF; + break; + default: + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, erc, 0, + (LPTSTR)&filename, sizeof(filename), NULL); + sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename); + rc = RC_FX; + } // endswitch erc + + } else { + if (!FindNextFile(Hsearch, &FileData)) { + DWORD erc = GetLastError(); + + if (erc != ERROR_NO_MORE_FILES) { + sprintf(g->Message, MSG(NEXT_FILE_ERROR), erc); + FindClose(Hsearch); + rc = RC_FX; + } else + rc = RC_EF; + + } else + iFile++; + + } // endif Hsearch + + if (rc == RC_OK) + _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype); + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base close routine for MUL access method. */ +/***********************************************************************/ +void TDBDHR::CloseDB(PGLOBAL g) + { + // Close the search handle. + if (!FindClose(Hsearch)) { + strcpy(g->Message, MSG(SRCH_CLOSE_ERR)); + longjmp(g->jumper[g->jump_level], GetAmType()); + } // endif FindClose + + iFile = 0; + Hsearch = INVALID_HANDLE_VALUE; + } // end of CloseDB + +// ------------------------ DHRCOL functions ---------------------------- + +/***********************************************************************/ +/* DHRCOL public constructor. */ +/***********************************************************************/ +DHRCOL::DHRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional DHR access method information for column. + N = cdp->GetOffset(); + } // end of DOSCOL constructor + +/***********************************************************************/ +/* DHRCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +DHRCOL::DHRCOL(DHRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + N = col1->N; + } // end of DHRCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the information */ +/* corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void DHRCOL::ReadColumn(PGLOBAL g) + { + int rc; + PTDBDHR tdbp = (PTDBDHR)To_Tdb; + +#ifdef DEBTRACE + fprintf(debug, + "DHR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n", + Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N); +#endif + + /*********************************************************************/ + /* Retrieve the information corresponding to the column number. */ + /*********************************************************************/ + switch (N) { + case 0: // Drive + Value->SetValue(Drive, _MAX_DRIVE); + break; + case 1: // Path + Value->SetValue(Direc, _MAX_DHR); + break; + case 2: // Name + Value->SetValue(Fname, _MAX_FNAME); + break; + case 3: // Extention + Value->SetValue(Ftype, _MAX_EXT); + break; + case 4: // Extention + Value->SetValue(tdbp->FileData.cAlternateFileName, 14); + break; + case 5: + Value->SetValue(tdbp->FileData.dwFileAttributes); + break; + case 6: + Value->SetValue(.................. + } // end of ReadColumn +#endif // 0 + diff --git a/storage/connect/tabmul.h b/storage/connect/tabmul.h new file mode 100644 index 00000000000..052b4e7d33e --- /dev/null +++ b/storage/connect/tabmul.h @@ -0,0 +1,220 @@ +/*************** Tabmul H Declares Source Code File (.H) ***************/ +/* Name: TABMUL.H Version 1.4 */ +/* */ +/* (C) Copyright to PlugDB Software Development 2003-2012 */ +/* Author: Olivier BERTRAND */ +/* */ +/* This file contains the TDBMUL and TDBDIR classes declares. */ +/***********************************************************************/ +#if defined(WIN32) +#include <io.h> +#else // !WIN32 +#include <sys/types.h> +#include <unistd.h> +#include <sys/stat.h> +#include <dirent.h> +#endif // !WIN32 +//#include "osutil.h" +#include "block.h" + +typedef class TDBMUL *PTDBMUL; +typedef class TDBSDR *PTDBSDR; + +/***********************************************************************/ +/* This is the MUL Access Method class declaration for files that are */ +/* physically split in multiple files having the same format. */ +/***********************************************************************/ +class DllExport TDBMUL : public TDBASE { +//friend class MULCOL; + public: + // Constructor + TDBMUL(PTDBASE tdbp); + TDBMUL(PTDBMUL tdbp); + + // Implementation + virtual AMT GetAmType(void) {return Tdbp->GetAmType();} + virtual PTDB Duplicate(PGLOBAL g); + + // Methods + virtual void ResetDB(void); + virtual PTDB CopyOne(PTABS t); + virtual bool IsSame(PTBX tp) {return tp == (PTBX)Tdbp;} + virtual PSZ GetFile(PGLOBAL g) {return Tdbp->GetFile(g);} + virtual int GetRecpos(void) {return 0;} + virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); + bool InitFileNames(PGLOBAL g); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + {strcpy(g->Message, MSG(MUL_MAKECOL_ERR)); return NULL;} + virtual int Cardinality(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual int GetProgMax(PGLOBAL g); + virtual int GetProgCur(void); + virtual int RowNumber(PGLOBAL g, bool b = false); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + + // Members + TDBASE *Tdbp; // Points to a (file) table class + char* *Filenames; // Points to file names + int Rows; // Total rows of already read files + int Mul; // Type of multiple file list + int NumFiles; // Number of physical files + int iFile; // Index of currently processed file + }; // end of class TDBMUL + +/***********************************************************************/ +/* Directory listing table. */ +/***********************************************************************/ +class DllExport DIRDEF : public TABDEF { /* Directory listing table */ + friend class CATALOG; + friend class TDBDIR; + public: + // Constructor + DIRDEF(void) {Fn = NULL; Incl = false; Huge = false;} + + // Implementation + virtual const char *GetType(void) {return "DIR";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + + protected: + // Members + PSZ Fn; /* Path/Name of file search */ + bool Incl; /* true to include sub-directories */ + bool Huge; /* true if files can be larger than 2GB */ + }; // end of DIRDEF + +/***********************************************************************/ +/* This is the DIR Access Method class declaration for tables that */ +/* represent a directory listing. The pathname is given at the create */ +/* time and can contain wildcard characters in the file name, and the */ +/* (virtual) table is populated when it is in use. */ +/***********************************************************************/ +class TDBDIR : public TDBASE { + friend class DIRCOL; + public: + // Constructor + TDBDIR(PDIRDEF tdp); + TDBDIR(PTDBDIR tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_DIR;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBDIR(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void) {return iFile;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);} + virtual int GetProgCur(void) {return iFile;} + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + char *Path(PGLOBAL g); + + // Members + PSZ To_File; // Points to file search pathname + int iFile; // Index of currently retrieved file +#if defined(WIN32) + _finddata_t FileData; // Find data structure + int Hsearch; // Search handle + char Drive[_MAX_DRIVE]; // Drive name +#else // !WIN32 + struct stat Fileinfo; // File info structure + struct dirent *Entry; // Point to directory entry structure + DIR *Dir; // To searched directory structure + bool Done; // true when _splipath is done + char Pattern[_MAX_FNAME+_MAX_EXT]; +#endif // !WIN32 + char Fpath[_MAX_PATH]; // Absolute file search pattern + char Direc[_MAX_DIR]; // Search path + char Fname[_MAX_FNAME]; // File name + char Ftype[_MAX_EXT]; // File extention + }; // end of class TDBDIR + +/***********************************************************************/ +/* This is the DIR Access Method class declaration for tables that */ +/* represent a directory listing. The pathname is given at the create */ +/* time and can contain wildcard characters in the file name, and the */ +/* (virtual) table is populated when it is in use. In addition, this */ +/* class also includes files of included sub-directories. */ +/***********************************************************************/ +class TDBSDR : public TDBDIR { + friend class DIRCOL; + public: + // Constructors + TDBSDR(PDIRDEF tdp) : TDBDIR(tdp) {Sub = NULL;} + TDBSDR(PTDBSDR tdbp); + + // Implementation + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBSDR(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + + // Database routines + virtual int GetMaxSize(PGLOBAL g); + virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);} + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); +//virtual void CloseDB(PGLOBAL g); + + protected: + int FindInDir(PGLOBAL g); + + typedef struct _Sub_Dir { + struct _Sub_Dir *Next; + struct _Sub_Dir *Prev; +#if defined(WIN32) + int H; // Search handle +#else // !WIN32 + DIR *D; +#endif // !WIN32 + size_t Len; // Initial directory name length + } SUBDIR, *PSUBDIR; + + // Members + PSUBDIR Sub; // To current Subdir block + }; // end of class TDBSDR + +/***********************************************************************/ +/* Class DIRCOL: DIR access method column descriptor. */ +/* This A.M. is used for tables populated by DIR file name list. */ +/***********************************************************************/ +class DIRCOL : public COLBLK { + public: + // Constructors + DIRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "DIR"); + DIRCOL(DIRCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_DIR;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + protected: + // Default constructor not to be used + DIRCOL(void) {} + + // Members + int N; // Column number + }; // end of class DIRCOL diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp new file mode 100644 index 00000000000..7b38cc8ab1b --- /dev/null +++ b/storage/connect/tabmysql.cpp @@ -0,0 +1,1102 @@ +/************* TabMySQL C++ Program Source Code File (.CPP) *************/ +/* PROGRAM NAME: TABMYSQL */ +/* ------------- */ +/* Version 1.7 */ +/* */ +/* AUTHOR: */ +/* ------- */ +/* Olivier BERTRAND 2007-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* Implements a table type that are MySQL tables. */ +/* It can optionally use the embedded MySQL library. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* TABMYSQL.CPP - Source code */ +/* PLGDBSEM.H - DB application declaration file */ +/* TABMYSQL.H - TABODBC classes declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/************************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//#include <windows.h> +#else // !WIN32 +//#include <fnmatch.h> +//#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "osutil.h" +//#include <io.h> +//#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "tabcol.h" +#include "colblk.h" +#include "mycat.h" +#include "reldef.h" +#include "tabmysql.h" +#include "valblk.h" + +#if defined(_CONSOLE) +void PrintResult(PGLOBAL, PSEM, PQRYRES); +#endif // _CONSOLE + +extern "C" int trace; + +/* -------------- Implementation of the MYSQLDEF class --------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +MYSQLDEF::MYSQLDEF(void) + { + Pseudo = 2; // SERVID is Ok but not ROWID + Hostname = NULL; + Database = NULL; + Tabname = NULL; + Username = NULL; + Password = NULL; + Portnumber = 0; + Bind = FALSE; + Delayed = FALSE; + } // end of MYSQLDEF constructor + +/***********************************************************************/ +/* Parse connection string */ +/* */ +/* SYNOPSIS */ +/* ParseURL() */ +/* url The connection string to parse */ +/* */ +/* DESCRIPTION */ +/* Populates the table with information about the connection */ +/* to the foreign database that will serve as the data source. */ +/* This string must be specified (currently) in the "CONNECTION" */ +/* field, listed in the CREATE TABLE statement. */ +/* */ +/* This string MUST be in the format of any of these: */ +/* */ +/* CONNECTION="scheme://user:pwd@host:port/database/table" */ +/* CONNECTION="scheme://user@host/database/table" */ +/* CONNECTION="scheme://user@host:port/database/table" */ +/* CONNECTION="scheme://user:pwd@host/database/table" */ +/* */ +/* _OR_ */ +/* */ +/* CONNECTION="connection name" (NIY) */ +/* */ +/* An Example: */ +/* */ +/* CREATE TABLE t1 (id int(32)) */ +/* ENGINE="CONNECT" TABLE_TYPE="MYSQL" */ +/* CONNECTION="mysql://joe:pwd@192.168.1.111:9308/dbname/tabname"; */ +/* */ +/* CREATE TABLE t2 ( */ +/* id int(4) NOT NULL auto_increment, */ +/* name varchar(32) NOT NULL, */ +/* PRIMARY KEY(id) */ +/* ) ENGINE="CONNECT" TABLE_TYPE="MYSQL" */ +/* CONNECTION="my_conn"; (NIY) */ +/* */ +/* 'password' and 'port' are both optional. */ +/* */ +/* RETURN VALUE */ +/* false success */ +/* true error */ +/* */ +/***********************************************************************/ +bool MYSQLDEF::ParseURL(PGLOBAL g, char *url) + { + if ((!strstr(url, "://") && (!strchr(url, '@')))) { + // No :// or @ in connection string. Must be a straight + // connection name of either "server" or "server/table" + strcpy(g->Message, "Using Federated server not implemented yet"); + return true; +#if 0 + /* ok, so we do a little parsing, but not completely! */ + share->parsed= FALSE; + /* + If there is a single '/' in the connection string, this means the user is + specifying a table name + */ + + if ((share->table_name= strchr(share->connection_string, '/'))) + { + *share->table_name++= '\0'; + share->table_name_length= strlen(share->table_name); + + DBUG_PRINT("info", + ("internal format, parsed table_name " + "share->connection_string: %s share->table_name: %s", + share->connection_string, share->table_name)); + + /* + there better not be any more '/'s ! + */ + if (strchr(share->table_name, '/')) + goto error; + } + /* + Otherwise, straight server name, use tablename of federatedx table + as remote table name + */ + else + { + /* + Connection specifies everything but, resort to + expecting remote and foreign table names to match + */ + share->table_name= strmake_root(mem_root, table->s->table_name.str, + (share->table_name_length= + table->s->table_name.length)); + DBUG_PRINT("info", + ("internal format, default table_name " + "share->connection_string: %s share->table_name: %s", + share->connection_string, share->table_name)); + } + + if ((error_num= get_connection(mem_root, share))) + goto error; +#endif // 0 + } else { + // URL, parse it + char *sport, *scheme = url; + + if (!(Username = strstr(url, "://"))) { + strcpy(g->Message, "Connection is not an URL"); + return true; + } // endif User + + scheme[Username - scheme] = 0; + + if (stricmp(scheme, "mysql")) { + strcpy(g->Message, "scheme must be mysql"); + return true; + } // endif scheme + + Username += 3; + + if (!(Hostname = strchr(Username, '@'))) { + strcpy(g->Message, "No host specified in URL"); + return true; + } else + *Hostname++ = 0; // End Username + + if ((Password = strchr(Username, ':'))) { + *Password++ = 0; // End username + + // Make sure there isn't an extra / or @ + if ((strchr(Password, '/') || strchr(Hostname, '@'))) { + strcpy(g->Message, "Syntax error in URL"); + return true; + } // endif + + // Found that if the string is: + // user:@hostname:port/db/table + // Then password is a null string, so set to NULL + if ((Password[0] == 0)) + Password = NULL; + + } // endif password + + // Make sure there isn't an extra / or @ */ + if ((strchr(Username, '/')) || (strchr(Hostname, '@'))) { + strcpy(g->Message, "Syntax error in URL"); + return true; + } // endif + + if ((Database = strchr(Hostname, '/'))) { + *Database++ = 0; + + if ((Tabname = strchr(Database, '/'))) + *Tabname++ = 0; + + // Make sure there's not an extra / + if ((strchr(Tabname, '/'))) { + strcpy(g->Message, "Syntax error in URL"); + return true; + } // endif / + + } // endif database + + if ((sport = strchr(Hostname, ':'))) + *sport++ = 0; + + Portnumber = (sport && sport[0]) ? atoi(sport) : MYSQL_PORT; + + if (Hostname[0] == 0) + Hostname = "localhost"; + + if (!Database || !*Database) + Database = Cat->GetStringCatInfo(g, "Database", "*"); + + if (!Tabname || !*Tabname) + Tabname = Name; + + } // endif URL + +#if 0 + if (!share->port) + if (!share->hostname || strcmp(share->hostname, my_localhost) == 0) + share->socket= (char *) MYSQL_UNIX_ADDR; + else + share->port= MYSQL_PORT; +#endif // 0 + + return false; + } // end of ParseURL + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XCV file. */ +/***********************************************************************/ +bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char *url = Cat->GetStringCatInfo(g, "Connect", NULL); + + Desc = "MySQL Table"; + + if (!url || !*url) { + // Not using the connection URL + Hostname = Cat->GetStringCatInfo(g, "Host", "localhost"); + Database = Cat->GetStringCatInfo(g, "Database", "*"); + Tabname = Cat->GetStringCatInfo(g, "Name", Name); // Deprecated + Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); + Username = Cat->GetStringCatInfo(g, "User", "root"); + Password = Cat->GetStringCatInfo(g, "Password", NULL); + Portnumber = Cat->GetIntCatInfo("Port", MYSQL_PORT); + } else if (ParseURL(g, url)) + return TRUE; + + Bind = !!Cat->GetIntCatInfo("Bind", 0); + Delayed = !!Cat->GetIntCatInfo("Delayed", 0); + return FALSE; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m) + { + if (Catfunc == FNC_COL) + return new(g) TDBMCL(this); + else + return new(g) TDBMYSQL(this); + + } // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBMYSQL class. */ +/***********************************************************************/ +TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp) + { + if (tdp) { + Host = tdp->GetHostname(); + Database = tdp->GetDatabase(); + Tabname = tdp->GetTabname(); + User = tdp->GetUsername(); + Pwd = tdp->GetPassword(); + Port = tdp->GetPortnumber(); + Prep = tdp->Bind; + Delayed = tdp->Delayed; + } else { + Host = NULL; + Database = NULL; + Tabname = NULL; + User = NULL; + Pwd = NULL; + Port = 0; + Prep = FALSE; + Delayed = FALSE; + } // endif tdp + + Bind = NULL; + Query = NULL; + Qbuf = NULL; + Fetched = FALSE; + m_Rc = RC_FX; + AftRows = 0; + N = -1; + Nparm = 0; + } // end of TDBMYSQL constructor + +TDBMYSQL::TDBMYSQL(PGLOBAL g, PTDBMY tdbp) : TDBASE(tdbp) + { + Host = tdbp->Host; + Database = tdbp->Database; + Tabname = tdbp->Tabname; + User = tdbp->User; + Pwd = tdbp->Pwd; + Port = tdbp->Port; + Prep = tdbp->Prep; + Delayed = tdbp->Delayed; + Bind = NULL; + Query = tdbp->Query; + Qbuf = NULL; + Fetched = tdbp->Fetched; + m_Rc = tdbp->m_Rc; + AftRows = tdbp->AftRows; + N = tdbp->N; + Nparm = tdbp->Nparm; + } // end of TDBMYSQL copy constructor + +// Is this really useful ??? +PTDB TDBMYSQL::CopyOne(PTABS t) + { + PTDB tp; + PCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBMYSQL(g, this); + + for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) { + cp2 = new(g) MYSQLCOL((PMYCOL)cp1, tp); + + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate MYSQL column description block. */ +/***********************************************************************/ +PCOL TDBMYSQL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) MYSQLCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* MakeSelect: make the Select statement use with MySQL connection. */ +/* Note: when implementing EOM filtering, column only used in local */ +/* filter should be removed from column list. */ +/***********************************************************************/ +bool TDBMYSQL::MakeSelect(PGLOBAL g) + { + char *colist; + char *tk = "`"; + int len = 0, ncol = 0, rank = 0; + bool b = FALSE; + PCOL colp; + PDBUSER dup = PlgGetUser(g); + + if (Query) + return FALSE; // already done + + for (colp = Columns; colp; colp = colp->GetNext()) + ncol++; + + if (ncol) { + colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol); + *colist = '\0'; + + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->IsSpecial()) { + strcpy(g->Message, MSG(NO_SPEC_COL)); + return TRUE; + } else { + if (b) + strcat(colist, ", "); + else + b = TRUE; + + strcat(strcat(strcat(colist, tk), colp->GetName()), tk); + ((PMYCOL)colp)->Rank = rank++; + } // endif colp + + } else { + // ncol == 0 can occur for queries such as Query count(*) from... + // for which we will count the rows from Query '*' from... + // (the use of a char constant minimize the result storage) + colist = (char*)PlugSubAlloc(g, NULL, 2); + strcpy(colist, "'*'"); + } // endif ncol + + // Below 32 is space to contain extra stuff + len += (strlen(colist) + strlen(Tabname) + 32); + len += (To_Filter ? strlen(To_Filter) + 7 : 0); + Query = (char*)PlugSubAlloc(g, NULL, len); + strcat(strcpy(Query, "SELECT "), colist); + strcat(strcat(strcat(strcat(Query, " FROM "), tk), Tabname), tk); + + if (To_Filter) + strcat(strcat(Query, " WHERE "), To_Filter); + + return FALSE; + } // end of MakeSelect + +/***********************************************************************/ +/* MakeInsert: make the Insert statement used with MySQL connection. */ +/***********************************************************************/ +bool TDBMYSQL::MakeInsert(PGLOBAL g) + { + char *colist, *valist = NULL; + char *tk = "`"; + int len = 0, qlen = 0; + bool b = FALSE; + PCOL colp; + + if (Query) + return FALSE; // already done + + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->IsSpecial()) { + strcpy(g->Message, MSG(NO_SPEC_COL)); + return TRUE; + } else { + len += (strlen(colp->GetName()) + 4); + ((PMYCOL)colp)->Rank = Nparm++; + } // endif colp + + colist = (char*)PlugSubAlloc(g, NULL, len); + *colist = '\0'; + + if (Prep) { +#if defined(MYSQL_PREPARED_STATEMENTS) + valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm); + *valist = '\0'; +#else // !MYSQL_PREPARED_STATEMENTS + strcpy(g->Message, "Prepared statements not used (not supported)"); + PushWarning(g, this); + Prep = FALSE; +#endif // !MYSQL_PREPARED_STATEMENTS + } // endif Prep + + for (colp = Columns; colp; colp = colp->GetNext()) { + if (b) { + strcat(colist, ", "); + if (Prep) strcat(valist, ","); + } else + b = TRUE; + + strcat(strcat(strcat(colist, tk), colp->GetName()), tk); + + // Parameter marker + if (!Prep) { + if (colp->GetResultType() == TYPE_DATE) + qlen += 20; + else + qlen += colp->GetLength(); + + } // endif Prep + + if (Prep) + strcat(valist, "?"); + + } // endfor colp + + // Below 40 is enough to contain the fixed part of the query + len = (strlen(Tabname) + strlen(colist) + + ((Prep) ? strlen(valist) : 0) + 40); + Query = (char*)PlugSubAlloc(g, NULL, len); + + if (Delayed) + strcpy(Query, "INSERT DELAYED INTO "); + else + strcpy(Query, "INSERT INTO "); + + strcat(strcat(strcat(Query, tk), Tabname), tk); + strcat(strcat(strcat(Query, " ("), colist), ") VALUES ("); + + if (Prep) + strcat(strcat(Query, valist), ")"); + else { + qlen += (strlen(Query) + Nparm); + Qbuf = (char *)PlugSubAlloc(g, NULL, qlen); + } // endelse Prep + + return FALSE; + } // end of MakeInsert + +#if 0 +/***********************************************************************/ +/* MakeUpdate: make the Update statement use with MySQL connection. */ +/* Note: currently limited to local values and filtering. */ +/***********************************************************************/ +bool TDBMYSQL::MakeUpdate(PGLOBAL g, PSELECT selist) + { + char *setlist, *colname, *where = NULL, *tk = "`"; + int len = 0, nset = 0; + bool b = FALSE; + PXOB xp; + PSELECT selp; + + if (Query) + return FALSE; // already done + + if (To_Filter) + if (To_Filter->CheckLocal(this)) { + where = (char*)PlugSubAlloc(g, NULL, 512); // Should be enough + *where = '\0'; + + if (!PlugRephraseSQL(g, where, To_Filter, TYPE_FILTER, tk)) + return TRUE; + + To_Filter = NULL; + len = strlen(where); + } else { + strcpy(g->Message, MSG(NO_REF_UPDATE)); + return TRUE; + } // endif Local + + for (selp = selist; selp; selp = selp->GetNext_Proj()) + nset++; + + assert(nset); + + // Allocate a pretty big buffer + setlist = (char*)PlugSubAlloc(g, NULL, 256 * nset); + *setlist = '\0'; + + for (selp = selist; selp; selp = selp->GetNext_Proj()) { + if (selp->GetSetType() == TYPE_COLBLK) { + colname = selp->GetSetCol()->GetName(); + } else if (selp->GetSetType() == TYPE_COLUMN) { + colname = (char*)((PCOLUMN)selp->GetSetCol())->GetName(); + } else { + sprintf(g->Message, MSG(BAD_SET_TYPE), selp->GetSetType()); + return TRUE; + } // endif Type + + if (b) + strcat(setlist, ", "); + else + b = TRUE; + + strcat(strcat(strcat(strcat(setlist, tk), colname), tk), " = "); + + xp = selp->GetObject(); + + if (!xp->CheckLocal(this)) { + strcpy(g->Message, MSG(NO_REF_UPDATE)); + return TRUE; + } else if (xp->GetType() == TYPE_SUBQ) + // Cannot be correlated because CheckLocal would have failed + xp = new(g) CONSTANT(xp->GetValue()); + + if (!PlugRephraseSQL(g, setlist + strlen(setlist), + xp, TYPE_XOBJECT, tk)) + return TRUE; + + } // endfor selp + + // Below 16 is enough to take care of the fixed part of the query + len += (strlen(setlist) + strlen(Tabname) + 16); + Query = (char*)PlugSubAlloc(g, NULL, len); + strcat(strcat(strcat(strcpy(Query, "UPDATE "), tk), Tabname), tk); + strcat(strcat(Query, " SET "), setlist); + + if (where) + strcat(Query, where); + + return FALSE; + } // end of MakeUpdate + +/***********************************************************************/ +/* MakeDelete: make the Delete statement use with MySQL connection. */ +/* If no filtering Truncate is used because it is faster than Delete. */ +/* However, the number of deleted lines is not returned by MySQL. */ +/* Note: currently limited to local filtering. */ +/***********************************************************************/ +bool TDBMYSQL::MakeDelete(PGLOBAL g) + { + char *tk = "`"; + int len = 0; + + if (Query) + return FALSE; // already done + + if (!To_Filter) + AftRows = -1; // Means "all lines deleted" + + // Below 16 is more than length of 'delete from ' + 3 + len += (strlen(Tabname) + 16); + len += (To_Filter ? strlen(To_Filter) + 7 : 0); + Query = (char*)PlugSubAlloc(g, NULL, len); + strcpy(Query, (To_Filter) ? "DELETE FROM " : "TRUNCATE "); + strcat(strcat(strcat(Query, tk), Tabname), tk); + + if (To_Filter) + strcat(strcat(Query, " WHERE "), To_Filter); + + return FALSE; + } // end of MakeDelete +#endif // 0 + +/***********************************************************************/ +/* XCV GetMaxSize: returns the maximum number of rows in the table. */ +/***********************************************************************/ +int TDBMYSQL::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { +#if 0 + if (MakeSelect(g)) + return -2; + + if (!Myc.Connected()) { + if (Myc.Open(g, Host, Database, User, Pwd, Port)) + return -1; + + } // endif connected + + if ((MaxSize = Myc.GetResultSize(g, Query)) < 0) { + Myc.Close(); + return -3; + } // endif MaxSize + + // FIXME: Columns should be known when Info calls GetMaxSize + if (!Columns) + Query = NULL; // Must be remade when columns are known +#endif // 0 + + MaxSize = 10; // To make MySQL happy + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* This a fake routine as ROWID does not exist in MySQL. */ +/***********************************************************************/ +int TDBMYSQL::RowNumber(PGLOBAL g, bool b) + { + return N; + } // end of RowNumber + +/***********************************************************************/ +/* Return 0 in mode DELETE to tell that the delete is done. */ +/***********************************************************************/ +int TDBMYSQL::GetProgMax(PGLOBAL g) + { + return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0 + : GetMaxSize(g); + } // end of GetProgMax + +/***********************************************************************/ +/* MySQL Bind Parameter function. */ +/***********************************************************************/ +int TDBMYSQL::BindColumns(PGLOBAL g) + { +#if defined(MYSQL_PREPARED_STATEMENTS) + if (Prep) { + Bind = (MYSQL_BIND*)PlugSubAlloc(g, NULL, Nparm * sizeof(MYSQL_BIND)); + + for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next) + colp->InitBind(g); + + return Myc.BindParams(g, Bind); + } // endif prep +#endif // MYSQL_PREPARED_STATEMENTS + + for (PMYCOL colp = (PMYCOL)Columns; colp; colp = (PMYCOL)colp->Next) + if (colp->Buf_Type == TYPE_DATE) + // Format must match DATETIME MySQL type + ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19); + + return RC_OK; + } // end of BindColumns + +/***********************************************************************/ +/* MySQL Access Method opening routine. */ +/***********************************************************************/ +bool TDBMYSQL::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + Myc.Rewind(); + return FALSE; + } // endif use + + /*********************************************************************/ + /* Open a MySQL connection for this table. */ + /* Note: this may not be the proper way to do. Perhaps it is better */ + /* to test whether a connection is already open for this server */ + /* and if so to allocate just a new result set. But this only for */ + /* servers allowing concurency in getting results ??? */ + /*********************************************************************/ + if (!Myc.Connected()) { + if (Myc.Open(g, Host, Database, User, Pwd, Port)) + return TRUE; + + } // endif Connected + + /*********************************************************************/ + /* Allocate whatever is used for getting results. */ + /*********************************************************************/ + if (Mode == MODE_READ) { + if (!MakeSelect(g)) + m_Rc = Myc.ExecSQL(g, Query); + + } else if (Mode == MODE_INSERT) { + if (!MakeInsert(g)) { +#if defined(MYSQL_PREPARED_STATEMENTS) + int n = (Prep) ? Myc.PrepareSQL(g, Query) : Nparm; + + if (Nparm != n) { + if (n >= 0) // Other errors return negative values + strcpy(g->Message, MSG(BAD_PARM_COUNT)); + + } else +#endif // MYSQL_PREPARED_STATEMENTS + m_Rc = BindColumns(g); + + } // endif MakeInsert + + if (m_Rc != RC_FX) { + char cmd[64]; + int w; + + sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname); + m_Rc = Myc.ExecSQL(g, cmd, &w); + } // endif m_Rc + +#if 0 + } else if (Next) { + strcpy(g->Message, MSG(NO_JOIN_UPDEL)); + } else if (Mode == MODE_DELETE) { + strcpy(g->Message, "MySQL table delete not implemented yet\n"); + bool rc = MakeDelete(g); + + if (!rc && Myc.ExecSQL(g, Query) == RC_NF) { + if (!AftRows) + AftRows = Myc.GetRows(); + + m_Rc = RC_OK; + } // endif ExecSQL +#endif // 0 + + } else { +// bool rc = MakeUpdate(g, sqlp->GetProj()); + strcpy(g->Message, "MySQL table delete/update not implemented yet\n"); + } // endelse + + if (m_Rc == RC_FX) { + Myc.Close(); + return TRUE; + } // endif rc + + Use = USE_OPEN; // Do it now in case we are recursively called + return FALSE; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for MYSQL access method. */ +/***********************************************************************/ +int TDBMYSQL::ReadDB(PGLOBAL g) + { + int rc; + + if (trace > 1) + htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", + GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); + + /*********************************************************************/ + /* Now start the reading process. */ + /* Here is the place to fetch the line. */ + /*********************************************************************/ + N++; + Fetched = ((rc = Myc.Fetch(g, -1)) == RC_OK); + + if (trace > 1) + htrc(" Read: rc=%d\n", rc); + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for MYSQL access methods. */ +/***********************************************************************/ +int TDBMYSQL::WriteDB(PGLOBAL g) + { +#if defined(MYSQL_PREPARED_STATEMENTS) + if (Prep) + return Myc.ExecStmt(g); +#endif // MYSQL_PREPARED_STATEMENTS + + // Statement was not prepared, we must construct and execute + // an insert query for each line to insert + int rc; + char buf[32]; + + strcpy(Qbuf, Query); + + // Make the Insert command value list + for (PCOL colp = Columns; colp; colp = colp->GetNext()) { + if (!colp->GetValue()->IsNull()) { + if (colp->GetResultType() == TYPE_STRING || + colp->GetResultType() == TYPE_DATE) + strcat(Qbuf, "'"); + + strcat(Qbuf, colp->GetValue()->GetCharString(buf)); + + if (colp->GetResultType() == TYPE_STRING || + colp->GetResultType() == TYPE_DATE) + strcat(Qbuf, "'"); + + } else + strcat(Qbuf, "NULL"); + + strcat(Qbuf, (colp->GetNext()) ? "," : ")"); + } // endfor colp + + Myc.m_Rows = -1; // To execute the query + rc = Myc.ExecSQL(g, Qbuf); + return (rc == RC_NF) ? RC_OK : rc; // RC_NF is Ok + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for MYSQL access methods. */ +/***********************************************************************/ +int TDBMYSQL::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(NO_MYSQL_DELETE)); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for MySQL access method. */ +/***********************************************************************/ +void TDBMYSQL::CloseDB(PGLOBAL g) + { + if (Mode == MODE_INSERT) { + char cmd[64]; + int w; + PDBUSER dup = PlgGetUser(g); + + dup->Step = "Enabling indexes"; + sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname); + Myc.m_Rows = -1; // To execute the query + m_Rc = Myc.ExecSQL(g, cmd, &w); + } // endif m_Rc + + Myc.Close(); + + if (trace) + htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc); + + } // end of CloseDB + +// ------------------------ MYSQLCOL functions -------------------------- + +/***********************************************************************/ +/* MYSQLCOL public constructor. */ +/***********************************************************************/ +MYSQLCOL::MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional Dos access method information for column. + Long = cdp->GetLong(); + Bind = NULL; + To_Val = NULL; + Slen = 0; + Rank = -1; // Not known yet + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of MYSQLCOL constructor + +/***********************************************************************/ +/* MYSQLCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +MYSQLCOL::MYSQLCOL(MYSQLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Long = col1->Long; + Bind = NULL; + To_Val = NULL; + Slen = col1->Slen; + Rank = col1->Rank; + } // end of MYSQLCOL copy constructor + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool MYSQLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return TRUE; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_FLOAT) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetPrecision()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return TRUE; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return TRUE; + + } // endif's Value, Buf_Type + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return FALSE; + } // end of SetBuffer + +/***********************************************************************/ +/* InitBind: Initialize the bind structure according to type. */ +/***********************************************************************/ +void MYSQLCOL::InitBind(PGLOBAL g) + { + PTDBMY tdbp = (PTDBMY)To_Tdb; + + assert(tdbp->Bind && Rank < tdbp->Nparm); + + Bind = &tdbp->Bind[Rank]; + memset(Bind, 0, sizeof(MYSQL_BIND)); + + if (Buf_Type == TYPE_DATE) { + // Default format must match DATETIME MySQL type +// if (!((DTVAL*)Value)->IsFormatted()) + ((DTVAL*)Value)->SetFormat(g, "YYYY-MM-DD hh:mm:ss", 19); + + Bind->buffer_type = PLGtoMYSQL(TYPE_STRING, false); + Bind->buffer = (char *)PlugSubAlloc(g,NULL, 20); + Bind->buffer_length = 20; + Bind->length = &Slen; + } else { + Bind->buffer_type = PLGtoMYSQL(Buf_Type, false); + Bind->buffer = (char *)Value->GetTo_Val(); + Bind->buffer_length = Value->GetClen(); + Bind->length = (IsTypeChar(Buf_Type)) ? &Slen : NULL; + } // endif Buf_Type + + } // end of InitBind + +/***********************************************************************/ +/* ReadColumn: */ +/***********************************************************************/ +void MYSQLCOL::ReadColumn(PGLOBAL g) + { + char *buf; + int rc; + PTDBMY tdbp = (PTDBMY)To_Tdb; + + if (trace) + htrc("MySQL ReadColumn: name=%s\n", Name); + + assert (Rank >= 0); + + /*********************************************************************/ + /* If physical fetching of the line was deferred, do it now. */ + /*********************************************************************/ + if (!tdbp->Fetched) + if ((rc = tdbp->Myc.Fetch(g, tdbp->N)) != RC_OK) { + if (rc == RC_EF) + sprintf(g->Message, MSG(INV_DEF_READ), rc); + + longjmp(g->jumper[g->jump_level], 11); + } else + tdbp->Fetched = TRUE; + + if ((buf = ((PTDBMY)To_Tdb)->Myc.GetCharField(Rank))) + Value->SetValue_char(buf, Long); + else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endelse + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: make sure the bind buffer is updated. */ +/***********************************************************************/ +void MYSQLCOL::WriteColumn(PGLOBAL g) + { + /*********************************************************************/ + /* Do convert the column value if necessary. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value + +#if defined(MYSQL_PREPARED_STATEMENTS) + if (((PTDBMY)To_Tdb)->Prep) { + if (Buf_Type == TYPE_DATE) { + Value->ShowValue((char *)Bind->buffer, (int)*Bind->length); + Slen = strlen((char *)Bind->buffer); + } else if (IsTypeChar(Buf_Type)) + Slen = strlen(Value->GetCharValue()); + + } // endif Prep +#endif // MYSQL_PREPARED_STATEMENTS + + } // end of WriteColumn + +/* ---------------------------TDBMCL class --------------------------- */ + +/***********************************************************************/ +/* TDBMCL class constructor. */ +/***********************************************************************/ +TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp) + { + Host = tdp->Hostname; + Db = tdp->Database; + Tab = tdp->Tabname; + User = tdp->Username; + Pwd = tdp->Password; + Port = tdp->Portnumber; + } // end of TDBMCL constructor + +/***********************************************************************/ +/* GetResult: Get the list the MYSQL table columns. */ +/***********************************************************************/ +PQRYRES TDBMCL::GetResult(PGLOBAL g) + { + return MyColumns(g, Host, Db, User, Pwd, Tab, NULL, Port, false, false); + } // end of GetResult diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h new file mode 100644 index 00000000000..56d21550df6 --- /dev/null +++ b/storage/connect/tabmysql.h @@ -0,0 +1,163 @@ +// TDBMYSQL.H Olivier Bertrand 2007-2013 +#include "myconn.h" // MySQL connection declares + +typedef class MYSQLDEF *PMYDEF; +typedef class TDBMYSQL *PTDBMY; +typedef class MYSQLC *PMYC; +typedef class MYSQLCOL *PMYCOL; + +/* ------------------------- MYSQL classes --------------------------- */ + +/***********************************************************************/ +/* MYSQL: table type that are MySQL tables. */ +/* Using embedded MySQL library (or optionally calling a MySQL server)*/ +/***********************************************************************/ + +/***********************************************************************/ +/* MYSQL table. */ +/***********************************************************************/ +class MYSQLDEF : public TABDEF {/* Logical table description */ + friend class TDBMYSQL; + friend class TDBMCL; + friend class ha_connect; + public: + // Constructor + MYSQLDEF(void); + + + // Implementation + virtual const char *GetType(void) {return "MYSQL";} + inline PSZ GetHostname(void) {return Hostname;}; + inline PSZ GetDatabase(void) {return Database;}; + inline PSZ GetTabname(void) {return Tabname;} + inline PSZ GetUsername(void) {return Username;}; + inline PSZ GetPassword(void) {return Password;}; + inline int GetPortnumber(void) {return Portnumber;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + bool ParseURL(PGLOBAL g, char *url); + + protected: + // Members + PSZ Hostname; /* Host machine to use */ + PSZ Database; /* Database to be used by server */ + PSZ Tabname; /* External table name */ + PSZ Username; /* User logon name */ + PSZ Password; /* Password logon info */ + int Portnumber; /* MySQL port number (0 = default) */ + bool Bind; /* Use prepared statement on insert */ + bool Delayed; /* Delayed insert */ + }; // end of MYSQLDEF + +/***********************************************************************/ +/* This is the class declaration for the MYSQL table. */ +/***********************************************************************/ +class TDBMYSQL : public TDBASE { + friend class MYSQLCOL; + public: + // Constructor + TDBMYSQL(PMYDEF tdp); + TDBMYSQL(PGLOBAL g, PTDBMY tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_MYSQL;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYSQL(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetAffectedRows(void) {return AftRows;} + virtual int GetRecpos(void) {return N;} + virtual int GetProgMax(PGLOBAL g); + virtual void ResetDB(void) {N = 0;} + virtual int RowNumber(PGLOBAL g, bool b = FALSE); + void SetDatabase(LPCSTR db) {Database = (char*)db;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Internal functions + bool MakeSelect(PGLOBAL g); + bool MakeInsert(PGLOBAL g); +//bool MakeUpdate(PGLOBAL g); +//bool MakeDelete(PGLOBAL g); + int BindColumns(PGLOBAL g); + + // Members + MYSQLC Myc; // MySQL connection class + MYSQL_BIND *Bind; // To the MySQL bind structure array + char *Host; // Host machine to use + char *User; // User logon info + char *Pwd; // Password logon info + char *Database; // Database to be used by server + char *Tabname; // External table name + char *Query; // Points to SQL query + char *Qbuf; // Used for not prepared insert + bool Fetched; // True when fetch was done + bool Prep; // Use prepared statement on insert + bool Delayed; // Use delayed insert + int m_Rc; // Return code from command + int AftRows; // The number of affected rows + int N; // The current table index + int Port; // MySQL port number (0 = default) + int Nparm; // The number of statement parameters + }; // end of class TDBMYSQL + +/***********************************************************************/ +/* Class MYSQLCOL: MySQL table column. */ +/***********************************************************************/ +class MYSQLCOL : public COLBLK { + friend class TDBMYSQL; + public: + // Constructors + MYSQLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "MYSQL"); + MYSQLCOL(MYSQLCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_MYSQL;} + void InitBind(PGLOBAL g); + + // Methods + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + + protected: + // Default constructor not to be used + MYSQLCOL(void) {} + + // Members + MYSQL_BIND *Bind; // This column bind structure pointer + PVAL To_Val; // To value used for Update/Insert + unsigned long Slen; // Bind string lengh + int Rank; // Rank (position) number in the query + }; // end of class MYSQLCOL + +/***********************************************************************/ +/* This is the class declaration for the MYSQL column catalog table. */ +/***********************************************************************/ +class TDBMCL : public TDBCAT { + public: + // Constructor + TDBMCL(PMYDEF tdp); + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + // Members + PSZ Host; // Host machine to use + PSZ Db; // Database to be used by server + PSZ Tab; // External table name + PSZ User; // User logon name + PSZ Pwd; // Password logon info + int Port; // MySQL port number (0 = default) + }; // end of class TDBMCL diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp new file mode 100644 index 00000000000..5bbc9effa92 --- /dev/null +++ b/storage/connect/tabodbc.cpp @@ -0,0 +1,960 @@ +/************* Tabodbc C++ Program Source Code File (.CPP) *************/ +/* PROGRAM NAME: TABODBC */ +/* ------------- */ +/* Version 2.5 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the TABODBC class DB execution routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* TABODBC.CPP - Source code */ +/* PLGDBSEM.H - DB application declaration file */ +/* TABODBC.H - TABODBC classes declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#include <sqltypes.h> +#else +#if defined(UNIX) +#include <errno.h> +#define NODW +#include "osutil.h" +#else +#include <io.h> +#endif +#include <fcntl.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* kindex.h is kindex header that also includes tabdos.h. */ +/* tabodbc.h is header containing the TABODBC class declarations. */ +/* odbconn.h is header containing ODBC connection declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "mycat.h" +#include "xtable.h" +#include "tabodbc.h" +#include "odbccat.h" +#include "tabmul.h" +#include "reldef.h" +#include "tabcol.h" +#include "valblk.h" + +#include "sql_string.h" + +extern "C" char *GetMsgid(int id); +extern "C" int trace; + +/***********************************************************************/ +/* DB static variables. */ +/***********************************************************************/ +// int num_read, num_there, num_eq[2], num_nf; // Statistics +extern int num_read, num_there, num_eq[2]; // Statistics + +/* -------------------------- Class ODBCDEF -------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +ODBCDEF::ODBCDEF(void) + { + Connect = Tabname = Tabowner = Tabqual = Qchar = NULL; + Catver = Options = 0; + } // end of ODBCDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + int dop = ODBConn::noOdbcDialog; // Default for options + + Desc = Connect = Cat->GetStringCatInfo(g, "Connect", ""); + Tabname = Cat->GetStringCatInfo(g, "Name", + (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); + Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); + Tabowner = Cat->GetStringCatInfo(g, "Owner", ""); + Tabqual = Cat->GetStringCatInfo(g, "Qualifier", ""); + Qchar = Cat->GetStringCatInfo(g, "Qchar", ""); + Catver = Cat->GetIntCatInfo("Catver", 2); + Options = Cat->GetIntCatInfo("Options", dop); + Pseudo = 2; // FILID is Ok but not ROWID + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m) + { + PTDBASE tdbp = NULL; + + /*********************************************************************/ + /* Allocate a TDB of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + switch (Catfunc) { + case FNC_COL: + tdbp = new(g) TDBOCL(this); + break; + case FNC_TABLE: + tdbp = new(g) TDBOTB(this); + break; + case FNC_DSN: + tdbp = new(g) TDBSRC(this); + break; + case FNC_DRIVER: + tdbp = new(g) TDBDRV(this); + break; + default: + tdbp = new(g) TDBODBC(this); + + if (Multiple == 1) + tdbp = new(g) TDBMUL(tdbp); + else if (Multiple == 2) + strcpy(g->Message, MSG(NO_ODBC_MUL)); + } // endswitch Catfunc + + return tdbp; + } // end of GetTable + +/* -------------------------- Class TDBODBC -------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBODBC class. */ +/***********************************************************************/ +TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp) + { + Ocp = NULL; + Cnp = NULL; + + if (tdp) { + Connect = tdp->GetConnect(); + TableName = tdp->GetTabname(); + Owner = tdp->GetTabowner(); + Qualifier = tdp->GetTabqual(); + Quote = tdp->GetQchar(); + Options = tdp->GetOptions(); + Rows = tdp->GetElemt(); + Catver = tdp->GetCatver(); + } else { + Connect = NULL; + TableName = NULL; + Owner = NULL; + Qualifier = NULL; + Quote = NULL; + Options = 0; + Rows = 0; + Catver = 0; + } // endif tdp + + Query = NULL; + Count = NULL; +//Where = NULL; + MulConn = NULL; + DBQ = NULL; + Fpos = 0; + AftRows = 0; + CurNum = 0; + Rbuf = 0; + BufSize = 0; + Nparm = 0; + } // end of TDBODBC standard constructor + +TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp) + { + Ocp = tdbp->Ocp; // is that right ? + Cnp = tdbp->Cnp; + Connect = tdbp->Connect; + TableName = tdbp->TableName; + Owner = tdbp->Owner; + Qualifier = tdbp->Qualifier; + Quote = tdbp->Quote; + Query = tdbp->Query; + Count = tdbp->Count; +//Where = tdbp->Where; + MulConn = tdbp->MulConn; + DBQ = tdbp->DBQ; + Options = tdbp->Options; + Rows = tdbp->Rows; + Fpos = tdbp->Fpos; + AftRows = tdbp->AftRows; +//Tpos = tdbp->Tpos; +//Spos = tdbp->Spos; + CurNum = tdbp->CurNum; + Rbuf = tdbp->Rbuf; + BufSize = tdbp->BufSize; + Nparm = tdbp->Nparm; + } // end of TDBODBC copy constructor + +// Method +PTDB TDBODBC::CopyOne(PTABS t) + { + PTDB tp; + PODBCCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBODBC(this); + + for (cp1 = (PODBCCOL)Columns; cp1; cp1 = (PODBCCOL)cp1->GetNext()) { + cp2 = new(g) ODBCCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate ODBC column description block. */ +/***********************************************************************/ +PCOL TDBODBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) ODBCCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* Extract the filename from connect string and return it. */ +/* This used for Multiple(1) tables. Also prepare a connect string */ +/* with a place holder to be used by SetFile. */ +/***********************************************************************/ +PSZ TDBODBC::GetFile(PGLOBAL g) + { + if (Connect) { + char *p1, *p2; + size_t n; + + if ((p1 = strstr(Connect, "DBQ="))) { + p1 += 4; // Beginning of file name + p2 = strchr(p1, ';'); // End of file path/name + + // Make the File path/name from the connect string + n = (p2) ? p2 - p1 : strlen(p1); + DBQ = (PSZ)PlugSubAlloc(g, NULL, n + 1); + memcpy(DBQ, p1, n); + DBQ[n] = '\0'; + + // Make the Format used to re-generate Connect (3 = "%s" + 1) + MulConn = (char*)PlugSubAlloc(g, NULL, strlen(Connect) - n + 3); + memcpy(MulConn, Connect, p1 - Connect); + MulConn[p1 - Connect] = '\0'; + strcat(strcat(MulConn, "%s"), (p2) ? p2 : ";"); + } // endif p1 + + } // endif Connect + + return (DBQ) ? DBQ : (PSZ)"???"; + } // end of GetFile + +/***********************************************************************/ +/* Set DBQ and get the new file name into the connect string. */ +/***********************************************************************/ +void TDBODBC::SetFile(PGLOBAL g, PSZ fn) + { + if (MulConn) { + int n = strlen(MulConn) + strlen(fn) - 1; + + if (n > BufSize) { + // Allocate a buffer larger than needed so the chance + // of having to reallocate it is reduced. + BufSize = n + 6; + Connect = (char*)PlugSubAlloc(g, NULL, BufSize); + } // endif n + + // Make the complete connect string + sprintf(Connect, MulConn, fn); + } // endif MultConn + + DBQ = fn; + } // end of SetFile + + +/******************************************************************/ +/* Convert an UTF-8 string to latin characters. */ +/******************************************************************/ +int TDBODBC::Decode(char *txt, char *buf, size_t n) +{ + uint dummy_errors; + uint32 len= copy_and_convert(buf, n, &my_charset_latin1, + txt, strlen(txt), + &my_charset_utf8_general_ci, + &dummy_errors); + buf[len]= '\0'; + return 0; +} // end of Decode + + +/***********************************************************************/ +/* MakeSQL: make the SQL statement use with ODBC connection. */ +/* Note: when implementing EOM filtering, column only used in local */ +/* filter should be removed from column list. */ +/***********************************************************************/ +char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt) + { + char *colist, *tabname, *sql, buf[64]; + LPCSTR ownp = NULL, qualp = NULL; + int rc, len, ncol = 0; + bool first = true; + PTABLE tablep = To_Table; + PCOL colp; + + if (!cnt) { + // Normal SQL statement to retrieve results + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) + ncol++; + + if (ncol) { + colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol); + + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + // Column name can be in UTF-8 encoding + rc= Decode(colp->GetName(), buf, sizeof(buf)); + + if (Quote) { + if (first) { + strcat(strcat(strcpy(colist, Quote), buf), Quote); + first = false; + } else + strcat(strcat(strcat(strcat(colist, ", "), + Quote), buf), Quote); + + } else { + if (first) { + strcpy(colist, buf); + first = false; + } else + strcat(strcat(colist, ", "), buf); + + } // endif Quote + + } // endif !Special + + } else { + // ncol == 0 can occur for queries such that sql count(*) from... + // for which we will count the rows from sql * from... + colist = (char*)PlugSubAlloc(g, NULL, 2); + strcpy(colist, "*"); + } // endif ncol + + } else { + // SQL statement used to retrieve the size of the result + colist = (char*)PlugSubAlloc(g, NULL, 9); + strcpy(colist, "count(*)"); + } // endif cnt + + // Table name can be encoded in UTF-8 + rc = Decode(TableName, buf, sizeof(buf)); + + // Put table name between identifier quotes in case in contains blanks + tabname = (char*)PlugSubAlloc(g, NULL, strlen(buf) + 3); + + if (Quote) + strcat(strcat(strcpy(tabname, Quote), buf), Quote); + else + strcpy(tabname, buf); + + // Below 14 is length of 'select ' + length of ' from ' + 1 + len = (strlen(colist) + strlen(buf) + 14); + len += (To_Filter ? strlen(To_Filter) + 7 : 0); + +// if (tablep->GetQualifier()) This is used when using a table +// qualp = tablep->GetQualifier(); from anotherPlugDB database but +// else makes no sense for ODBC. + if (Qualifier && *Qualifier) + qualp = Qualifier; + + if (qualp) + len += (strlen(qualp) + 2); + + if (tablep->GetCreator()) + ownp = tablep->GetCreator(); + else if (Owner && *Owner) + ownp = Owner; + + if (ownp) + len += (strlen(ownp) + 1); + + sql = (char*)PlugSubAlloc(g, NULL, len); + strcat(strcat(strcpy(sql, "SELECT "), colist), " FROM "); + + if (qualp) { + strcat(sql, qualp); + + if (ownp) + strcat(strcat(sql, "."), ownp); + else + strcat(sql, "."); + + strcat(sql, "."); + } else if (ownp) + strcat(strcat(sql, ownp), "."); + + strcat(sql, tabname); + + if (To_Filter) + strcat(strcat(sql, " WHERE "), To_Filter); + + return sql; + } // end of MakeSQL + +/***********************************************************************/ +/* ResetSize: call by TDBMUL when calculating size estimate. */ +/***********************************************************************/ +void TDBODBC::ResetSize(void) + { + MaxSize = -1; + + if (Ocp && Ocp->IsOpen()) + Ocp->Close(); + + } // end of ResetSize + +/***********************************************************************/ +/* ODBC GetMaxSize: returns table size estimate in number of lines. */ +/***********************************************************************/ +int TDBODBC::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + if (!Ocp) + Ocp = new(g) ODBConn(g, this); + + if (!Ocp->IsOpen()) + if (Ocp->Open(Connect, Options) < 1) + return -1; + + if (!Count && !(Count = MakeSQL(g, true))) + return -2; + + if (!Cnp) { + // Allocate a Count(*) column (must not use the default constructor) + Cnp = new(g) ODBCCOL; + Cnp->InitValue(g); + } // endif Cnp + + if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0) + return -3; + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Return 0 in mode DELETE or UPDATE to tell that it is done. */ +/***********************************************************************/ +int TDBODBC::GetProgMax(PGLOBAL g) + { + return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0 + : GetMaxSize(g); + } // end of GetProgMax + +/***********************************************************************/ +/* ODBC Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBODBC::OpenDB(PGLOBAL g) + { + bool rc = false; + + if (g->Trace) + htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n", + this, Tdb_No, Use, Mode); + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ +// if (To_Kindex) + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ +// To_Kindex->Reset(); + +// rewind(Stream); >>>>>>> Something to be done with Cursor <<<<<<< + return false; + } // endif use + + /*********************************************************************/ + /* Open an ODBC connection for this table. */ + /* Note: this may not be the proper way to do. Perhaps it is better */ + /* to test whether a connection is already open for this datasource */ + /* and if so to allocate just a new result set. But this only for */ + /* drivers allowing concurency in getting results ??? */ + /*********************************************************************/ + if (!Ocp) + Ocp = new(g) ODBConn(g, this); + else if (Ocp->IsOpen()) + Ocp->Close(); + + if (Ocp->Open(Connect, Options) < 1) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Allocate whatever is used for getting results. */ + /*********************************************************************/ + if (Mode == MODE_READ) { + /*******************************************************************/ + /* The issue here is that if max result size is needed, it must be */ + /* calculated before the result set for the final data retrieval is*/ + /* allocated and the final statement prepared so we call GetMaxSize*/ + /* here. It can be a waste of time if the max size is not needed */ + /* but currently we always are asking for it (for progress info). */ + /*******************************************************************/ + GetMaxSize(g); // Will be set for next call + + if (!Query) + if ((Query = MakeSQL(g, false))) { + for (PODBCCOL colp = (PODBCCOL)Columns; + colp; colp = (PODBCCOL)colp->GetNext()) + if (!colp->IsSpecial()) + colp->AllocateBuffers(g, Rows); + + } else + rc = true; + + if (!rc) + rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0); + + } else { + strcpy(g->Message, "ODBC tables are read only in this version"); + return true; + } // endelse + + if (rc) { + Ocp->Close(); + return true; + } // endif rc + + /*********************************************************************/ + /* Reset statistics values. */ + /*********************************************************************/ + num_read = num_there = num_eq[0] = num_eq[1] = 0; + return false; + } // end of OpenDB + +/***********************************************************************/ +/* GetRecpos: return the position of last read record. */ +/***********************************************************************/ +int TDBODBC::GetRecpos(void) + { + return Fpos; // To be really implemented + } // end of GetRecpos + +/***********************************************************************/ +/* VRDNDOS: Data Base read routine for odbc access method. */ +/***********************************************************************/ +int TDBODBC::ReadDB(PGLOBAL g) + { + int rc; + + if (trace > 1) + htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", + GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); + + if (To_Kindex) { + // Direct access of ODBC tables is not implemented yet + strcpy(g->Message, MSG(NO_ODBC_DIRECT)); + longjmp(g->jumper[g->jump_level], GetAmType()); + +#if 0 + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as current one + num_there++; + return RC_OK; + default: + /***************************************************************/ + /* Set the cursor position according to record to read. */ + /***************************************************************/ +//--------------------------------- TODO -------------------------------- + break; + } // endswitch recpos +#endif // 0 + + } // endif To_Kindex + + /*********************************************************************/ + /* Now start the reading process. */ + /* Here is the place to fetch the line(s). */ + /*********************************************************************/ + if (++CurNum >= Rbuf) { + Rbuf = Ocp->Fetch(); + CurNum = 0; + } // endif CurNum + + rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX; + Fpos++; // Used for progress info + + if (trace > 1) + htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc); + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base Insert write routine for ODBC access method. */ +/***********************************************************************/ +int TDBODBC::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "ODBC tables are read only"); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for ODBC access method. */ +/***********************************************************************/ +int TDBODBC::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(NO_ODBC_DELETE)); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for ODBC access method. */ +/***********************************************************************/ +void TDBODBC::CloseDB(PGLOBAL g) + { +//if (To_Kindex) { +// To_Kindex->Close(); +// To_Kindex = NULL; +// } // endif + + Ocp->Close(); + + if (trace) + htrc("ODBC CloseDB: closing %s\n", Name); + + } // end of CloseDB + +/* --------------------------- ODBCCOL ------------------------------- */ + +/***********************************************************************/ +/* ODBCCOL public constructor. */ +/***********************************************************************/ +ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional ODBC access method information for column. + Long = cdp->GetLong(); +//strcpy(F_Date, cdp->F_Date); + To_Val = NULL; + Slen = 0; + StrLen = &Slen; + Sqlbuf = NULL; + Bufp = NULL; + Blkp = NULL; + Rank = 0; // Not known yet + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of ODBCCOL constructor + +/***********************************************************************/ +/* ODBCCOL private constructor. */ +/***********************************************************************/ +ODBCCOL::ODBCCOL(void) : COLBLK() + { + Buf_Type = TYPE_INT; // This is a count(*) column + // Set additional Dos access method information for column. + Long = sizeof(int); + To_Val = NULL; + Slen = 0; + StrLen = &Slen; + Sqlbuf = NULL; + Bufp = NULL; + Blkp = NULL; + Rank = 1; + } // end of ODBCCOL constructor + +/***********************************************************************/ +/* ODBCCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Long = col1->Long; +//strcpy(F_Date, col1->F_Date); + To_Val = col1->To_Val; + Slen = col1->Slen; + StrLen = col1->StrLen; + Sqlbuf = col1->Sqlbuf; + Bufp = col1->Bufp; + Blkp = col1->Blkp; + Rank = col1->Rank; + } // end of ODBCCOL copy constructor + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool ODBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_FLOAT) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetPrecision()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* ReadColumn: when SQLFetch is used there is nothing to do as the */ +/* column buffer was bind to the record set. This is also the case */ +/* when calculating MaxSize (Bufp is NULL even when Rows is not). */ +/***********************************************************************/ +void ODBCCOL::ReadColumn(PGLOBAL g) + { + PTDBODBC tdbp = (PTDBODBC)To_Tdb; + int n = tdbp->CurNum; + + if (StrLen[n] == SQL_NULL_DATA) { + // Null value + if (Nullable) + Value->SetNull(true); + + Value->Reset(); + return; + } else + Value->SetNull(false); + + if (Bufp && tdbp->Rows) + if (Buf_Type == TYPE_DATE) + *Sqlbuf = ((TIMESTAMP_STRUCT*)Bufp)[n]; + else + Value->SetValue_pvblk(Blkp, n); + + if (Buf_Type == TYPE_DATE) { + struct tm dbtime = {0,0,0,0,0,0,0,0,0}; + + dbtime.tm_sec = (int)Sqlbuf->second; + dbtime.tm_min = (int)Sqlbuf->minute; + dbtime.tm_hour = (int)Sqlbuf->hour; + dbtime.tm_mday = (int)Sqlbuf->day; + dbtime.tm_mon = (int)Sqlbuf->month - 1; + dbtime.tm_year = (int)Sqlbuf->year - 1900; + ((DTVAL*)Value)->MakeTime(&dbtime); + } // endif Buf_Type + + if (g->Trace) { + char buf[32]; + + htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n", + Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf)); + } // endif Trace + + } // end of ReadColumn + +/***********************************************************************/ +/* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */ +/* or Fetch. Note: we use Long+1 here because ODBC must have space */ +/* for the ending null character. */ +/***********************************************************************/ +void ODBCCOL::AllocateBuffers(PGLOBAL g, int rows) + { + if (Buf_Type == TYPE_DATE) + Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL, + sizeof(TIMESTAMP_STRUCT)); + + if (!rows) + return; + + if (Buf_Type == TYPE_DATE) + Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT)); + else { + Blkp = AllocValBlock(g, NULL, Buf_Type, rows, Long+1, 0, true, false); + Bufp = Blkp->GetValPointer(); + } // endelse + + if (rows > 1) + StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(int)); + + } // end of AllocateBuffers + +/***********************************************************************/ +/* Returns the buffer to use for Fetch or Extended Fetch. */ +/***********************************************************************/ +void *ODBCCOL::GetBuffer(DWORD rows) + { + if (rows && To_Tdb) { + assert(rows == (DWORD)((TDBODBC*)To_Tdb)->Rows); + return Bufp; + } else + return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val(); + + } // end of GetBuffer + +/***********************************************************************/ +/* Returns the buffer length to use for Fetch or Extended Fetch. */ +/***********************************************************************/ +SWORD ODBCCOL::GetBuflen(void) + { + if (Buf_Type == TYPE_DATE) + return (SWORD)sizeof(TIMESTAMP_STRUCT); + else if (Buf_Type == TYPE_STRING) + return (SWORD)Value->GetClen() + 1; + else + return (SWORD)Value->GetClen(); + + } // end of GetBuflen + +/***********************************************************************/ +/* WriteColumn: make sure the bind buffer is updated. */ +/***********************************************************************/ +void ODBCCOL::WriteColumn(PGLOBAL g) + { + /*********************************************************************/ + /* Do convert the column value if necessary. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the inserted value + + if (Buf_Type == TYPE_DATE) { + struct tm *dbtime = ((DTVAL*)Value)->GetGmTime(); + + Sqlbuf->second = dbtime->tm_sec; + Sqlbuf->minute = dbtime->tm_min; + Sqlbuf->hour = dbtime->tm_hour; + Sqlbuf->day = dbtime->tm_mday; + Sqlbuf->month = dbtime->tm_mon + 1; + Sqlbuf->year = dbtime->tm_year + 1900; + } // endif Buf_Type + + } // end of WriteColumn + +/* ---------------------------TDBSRC class --------------------------- */ + +/***********************************************************************/ +/* GetResult: Get the list of ODBC data sources. */ +/***********************************************************************/ +PQRYRES TDBSRC::GetResult(PGLOBAL g) + { + return ODBCDataSources(g, false); + } // end of GetResult + +/* ---------------------------TDBDRV class --------------------------- */ + +/***********************************************************************/ +/* GetResult: Get the list of ODBC drivers. */ +/***********************************************************************/ +PQRYRES TDBDRV::GetResult(PGLOBAL g) + { + return ODBCDrivers(g, false); + } // end of GetResult + +/* ---------------------------TDBOTB class --------------------------- */ + +/***********************************************************************/ +/* TDBOTB class constructor. */ +/***********************************************************************/ +TDBOTB::TDBOTB(PODEF tdp) : TDBCAT(tdp) + { + Dsn = tdp->GetConnect(); + Tab = tdp->GetTabname(); + } // end of TDBOTB constructor + +/***********************************************************************/ +/* GetResult: Get the list of ODBC tables. */ +/***********************************************************************/ +PQRYRES TDBOTB::GetResult(PGLOBAL g) + { + return ODBCTables(g, Dsn, Tab, false); + } // end of GetResult + +/* ---------------------------TDBOCL class --------------------------- */ + +/***********************************************************************/ +/* GetResult: Get the list of ODBC table columns. */ +/***********************************************************************/ +PQRYRES TDBOCL::GetResult(PGLOBAL g) + { + return ODBCColumns(g, Dsn, Tab, NULL, false); + } // end of GetResult + +/* ------------------------ End of Tabodbc --------------------------- */ diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h new file mode 100644 index 00000000000..645426eef78 --- /dev/null +++ b/storage/connect/tabodbc.h @@ -0,0 +1,225 @@ +/*************** Tabodbc H Declares Source Code File (.H) **************/ +/* Name: TABODBC.H Version 1.5 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */ +/* */ +/* This file contains the TDBODBC classes declares. */ +/***********************************************************************/ +#include "colblk.h" +#include "resource.h" + +typedef class ODBCDEF *PODEF; +typedef class TDBODBC *PTDBODBC; +typedef class ODBCCOL *PODBCCOL; +typedef class TDBOIF *PTDBOIF; +typedef class OIFCOL *POIFCOL; +typedef class TDBSRC *PTDBSRC; + +/***********************************************************************/ +/* ODBC table. */ +/***********************************************************************/ +class DllExport ODBCDEF : public TABDEF { /* Logical table description */ + public: + // Constructor + ODBCDEF(void); + + // Implementation + virtual const char *GetType(void) {return "ODBC";} + PSZ GetConnect(void) {return Connect;} + PSZ GetTabname(void) {return Tabname;} + PSZ GetTabowner(void) {return Tabowner;} + PSZ GetTabqual(void) {return Tabqual;} + PSZ GetQchar(void) {return (Qchar && *Qchar) ? Qchar : NULL;} + int GetCatver(void) {return Catver;} + int GetOptions(void) {return Options;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + + protected: + // Members + PSZ Connect; /* ODBC connection string */ + PSZ Tabname; /* External table name */ + PSZ Tabowner; /* External table owner */ + PSZ Tabqual; /* External table qualifier */ + PSZ Qchar; /* Identifier quoting character */ + int Catver; /* ODBC version for catalog functions */ + int Options; /* Open connection options */ + }; // end of ODBCDEF + +#if !defined(NODBC) +#include "odbconn.h" + +/***********************************************************************/ +/* This is the ODBC Access Method class declaration for files from */ +/* other DB drivers to be accessed via ODBC. */ +/***********************************************************************/ +class TDBODBC : public TDBASE { + friend class ODBCCOL; + friend class ODBConn; + public: + // Constructor + TDBODBC(PODEF tdp = NULL); + TDBODBC(PTDBODBC tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ODBC;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBODBC(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void); + virtual PSZ GetFile(PGLOBAL g); + virtual void SetFile(PGLOBAL g, PSZ fn); + virtual void ResetSize(void); + virtual int GetAffectedRows(void) {return AftRows;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetProgMax(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Internal functions + int Decode(char *utf, char *buf, size_t n); + char *MakeSQL(PGLOBAL g, bool cnt); +//bool MakeUpdate(PGLOBAL g, PSELECT selist); +//bool MakeInsert(PGLOBAL g); +//bool MakeDelete(PGLOBAL g); +//bool MakeFilter(PGLOBAL g, bool c); +//bool BindParameters(PGLOBAL g); + + // Members + ODBConn *Ocp; // Points to an ODBC connection class + ODBCCOL *Cnp; // Points to count(*) column + char *Connect; // Points to connection string + char *TableName; // Points to ODBC table name + char *Owner; // Points to ODBC table Owner + char *Qualifier; // Points to ODBC table Qualifier + char *Query; // Points to SQL statement + char *Count; // Points to count(*) SQL statement +//char *Where; // Points to local where clause + char *Quote; // The identifier quoting character + char *MulConn; // Used for multiple ODBC tables + char *DBQ; // The address part of Connect string + int Options; // Connect options + int Fpos; // Position of last read record + int AftRows; // The number of affected rows + int Rows; // Rowset size + int Catver; // Catalog ODBC version + int CurNum; // Current buffer line number + int Rbuf; // Number of lines read in buffer + int BufSize; // Size of connect string buffer + int Nparm; // The number of statement parameters + }; // end of class TDBODBC + +/***********************************************************************/ +/* Class ODBCCOL: DOS access method column descriptor. */ +/* This A.M. is used for ODBC tables. */ +/***********************************************************************/ +class ODBCCOL : public COLBLK { + friend class TDBODBC; + public: + // Constructors + ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "ODBC"); + ODBCCOL(ODBCCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_ODBC;} + SQLLEN *GetStrLen(void) {return StrLen;} + int GetRank(void) {return Rank;} +// PVBLK GetBlkp(void) {return Blkp;} + + // Methods +//virtual bool CheckLocal(PTDB tdbp); + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + void AllocateBuffers(PGLOBAL g, int rows); + void *GetBuffer(DWORD rows); + SWORD GetBuflen(void); +// void Print(PGLOBAL g, FILE *, uint); + + protected: + // Constructor used by GetMaxSize + ODBCCOL(void); + + // Members + TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's + void *Bufp; // To extended buffer + PVBLK Blkp; // To Value Block +//char F_Date[12]; // Internal Date format + PVAL To_Val; // To value used for Insert + SQLLEN *StrLen; // As returned by ODBC + SQLLEN Slen; // Used with Fetch + int Rank; // Rank (position) number in the query + }; // end of class ODBCCOL + +/***********************************************************************/ +/* This is the class declaration for the Data Sources catalog table. */ +/***********************************************************************/ +class TDBSRC : public TDBCAT { + public: + // Constructor + TDBSRC(PODEF tdp) : TDBCAT(tdp) {} + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + }; // end of class TDBSRC + +/***********************************************************************/ +/* This is the class declaration for the Drivers catalog table. */ +/***********************************************************************/ +class TDBDRV : public TDBCAT { + public: + // Constructor + TDBDRV(PODEF tdp) : TDBCAT(tdp) {} + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + }; // end of class TDBDRV + +/***********************************************************************/ +/* This is the class declaration for the tables catalog table. */ +/***********************************************************************/ +class TDBOTB : public TDBCAT { + public: + // Constructor + TDBOTB(PODEF tdp); + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + // Members + char *Dsn; // Points to connection string + char *Tab; // Points to ODBC table name or pattern + }; // end of class TDBOTB + +/***********************************************************************/ +/* This is the class declaration for the columns catalog table. */ +/***********************************************************************/ +class TDBOCL : public TDBOTB { + public: + // Constructor + TDBOCL(PODEF tdp) : TDBOTB(tdp) {} + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + // Members + }; // end of class TDBOCL + +#endif // !NODBC diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp new file mode 100644 index 00000000000..fccd8338d8a --- /dev/null +++ b/storage/connect/tabpivot.cpp @@ -0,0 +1,1202 @@ +/************ TabPivot C++ Program Source Code File (.CPP) *************/ +/* PROGRAM NAME: TABPIVOT */ +/* ------------- */ +/* Version 1.3 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the PIVOT classes DB execution routines. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the operating system header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#elif defined(UNIX) +#include <errno.h> +#include <unistd.h> +#include "osutil.h" +#else +#include <io.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/***********************************************************************/ +#define FRM_VER 6 +#include "table.h" // MySQL table definitions +#include "sql_const.h" +#include "field.h" +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "xindex.h" +#include "colblk.h" +#include "tabmysql.h" +#include "csort.h" +#include "tabpivot.h" +#include "valblk.h" +#include "ha_connect.h" +#include "mycat.h" // For GetHandler + +extern "C" int trace; + +/* --------------- Implementation of the PIVOT classes --------------- */ + +/***********************************************************************/ +/* DefineAM: define specific AM block values from PIVOT table. */ +/***********************************************************************/ +bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char *p1, *p2; + PHC hc = ((MYCAT*)Cat)->GetHandler(); + + Host = Cat->GetStringCatInfo(g, Name, "Host", "localhost"); + User = Cat->GetStringCatInfo(g, Name, "User", "root"); + Pwd = Cat->GetStringCatInfo(g, Name, "Password", NULL); + DB = Cat->GetStringCatInfo(g, Name, "Database", (PSZ)hc->GetDBName(NULL)); + Tabsrc = Cat->GetStringCatInfo(g, Name, "SrcDef", NULL); + Tabname = Cat->GetStringCatInfo(g, Name, "Name", NULL); + Picol = Cat->GetStringCatInfo(g, Name, "PivotCol", NULL); + Fncol = Cat->GetStringCatInfo(g, Name, "FncCol", NULL); + + // If fncol is like avg(colname), separate Fncol and Function + if (Fncol && (p1 = strchr(Fncol, '(')) && (p2 = strchr(p1, ')')) && + (*Fncol != '"') && (!*(p2+1))) { + *p1++ = '\0'; *p2 = '\0'; + Function = Fncol; + Fncol = p1; + } else + Function = Cat->GetStringCatInfo(g, Name, "Function", "SUM"); + + GBdone = Cat->GetIntCatInfo(Name, "Groupby", 0) ? TRUE : FALSE; + Port = Cat->GetIntCatInfo(Name, "Port", 3306); + Desc = (*Tabname) ? Tabname : Tabsrc; + return FALSE; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB PIVOTDEF::GetTable(PGLOBAL g, MODE m) + { + return new(g) TDBPIVOT(this); + } // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBPIVOT class. */ +/***********************************************************************/ +TDBPIVOT::TDBPIVOT(PPIVOTDEF tdp) : TDBASE(tdp), CSORT(FALSE) + { + Tqrp = NULL; + Host = tdp->Host; + Database = tdp->DB; + User = tdp->User; + Pwd = tdp->Pwd; + Port = tdp->Port; + Qryp = NULL; + Tabname = tdp->Tabname; // Name of source table + Tabsrc = tdp->Tabsrc; // SQL description of source table + Picol = tdp->Picol; // Pivot column name + Fncol = tdp->Fncol; // Function column name + Function = tdp->Function; // Aggregate function name + Xcolp = NULL; // To the FNCCOL column +//Xresp = NULL; // To the pivot result column +//Rblkp = NULL; // The value block of the pivot column + Fcolp = NULL; // To the function column + GBdone = tdp->GBdone; + Mult = -1; // Estimated table size + N = 0; // The current table index + M = 0; // The occurence rank + FileStatus = 0; // Logical End-of-File + RowFlag = 0; // 0: Ok, 1: Same, 2: Skip + } // end of TDBPIVOT constructor + +#if 0 +TDBPIVOT::TDBPIVOT(PTDBPIVOT tdbp) : TDBASE(tdbp), CSORT(FALSE) + { + Tdbp = tdbp->Tdbp; + Sqlp = tdbp->Sqlp; + Qryp = tdbp->Qryp; + Tabname = tdbp->Tabname; + Tabsrc = tdbp->Tabsrc; + Picol = tdbp->Picol; + Fncol = tdbp->Fncol; + Function = tdbp->Function; + Xcolp = tdbp->Xcolp; + Xresp = tdbp->Xresp; + Rblkp = tdbp->Rblkp; + Fcolp = tdbp->Fcolp; + Mult = tdbp->Mult; + N = tdbp->N; + M = tdbp->M; + FileStatus = tdbp->FileStatus; + RowFlag = tdbp->RowFlag; + } // end of TDBPIVOT copy constructor + +// Is this really useful ??? +PTDB TDBPIVOT::CopyOne(PTABS t) + { + PTDB tp = new(t->G) TDBPIVOT(this); + + tp->SetColumns(Columns); + return tp; + } // end of CopyOne +#endif // 0 + +/***********************************************************************/ +/* Prepare the source table Query. */ +/***********************************************************************/ +PQRYRES TDBPIVOT::GetSourceTable(PGLOBAL g) + { + if (Qryp) + return Qryp; // Already done + + if (!Tabsrc && Tabname) { + char *def, *colist; + size_t len = 0; + PCOL colp; + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + // Evaluate the length of the column list + for (colp = Columns; colp; colp = colp->GetNext()) + len += (strlen(colp->GetName()) + 2); + + *(colist = (char*)PlugSubAlloc(g, NULL, len)) = 0; + + // Locate the suballocated string (size is not known yet) + def = (char*)PlugSubAlloc(g, NULL, 0); + strcpy(def, "SELECT "); + + if (!Fncol) { + for (colp = Columns; colp; colp = colp->GetNext()) + if (!Picol || stricmp(Picol, colp->GetName())) + Fncol = colp->GetName(); + + if (!Fncol) { + strcpy(g->Message, MSG(NO_DEF_FNCCOL)); + return NULL; + } // endif Fncol + + } else if (!(ColDB(g, Fncol, 0))) { + // Function column not found in table + sprintf(g->Message, MSG(COL_ISNOT_TABLE), Fncol, Tabname); + return NULL; + } // endif Fcolp + + if (!Picol) { + // Find default Picol as the last one not equal to Fncol + for (colp = Columns; colp; colp = colp->GetNext()) + if (!Fncol || stricmp(Fncol, colp->GetName())) + Picol = colp->GetName(); + + if (!Picol) { + strcpy(g->Message, MSG(NO_DEF_PIVOTCOL)); + return NULL; + } // endif Picol + + } else if (!(ColDB(g, Picol, 0))) { + // Pivot column not found in table + sprintf(g->Message, MSG(COL_ISNOT_TABLE), Picol, Tabname); + return NULL; + } // endif Xcolp + + // Make the other column list + for (colp = Columns; colp; colp = colp->GetNext()) + if (stricmp(Picol, colp->GetName()) && + stricmp(Fncol, colp->GetName())) + strcat(strcat(colist, colp->GetName()), ", "); + + // Add the Pivot column at the end of the list + strcat(strcat(def, strcat(colist, Picol)), ", "); + + // Continue making the definition + if (!GBdone) { + // Make it suitable for Pivot by doing the group by + strcat(strcat(def, Function), "("); + strcat(strcat(strcat(def, Fncol), ") "), Fncol); + strcat(strcat(def, " FROM "), Tabname); + strcat(strcat(def, " GROUP BY "), colist); + } else // Gbdone + strcat(strcat(strcat(def, Fncol), " FROM "), Tabname); + + // Now we know how much was suballocated + Tabsrc = (char*)PlugSubAlloc(g, NULL, strlen(def)); + } else { + strcpy(g->Message, MSG(SRC_TABLE_UNDEF)); + return NULL; + } // endif Tabsrc + + int w; + + // Open a MySQL connection for this table + if (Myc.Open(g, Host, Database, User, Pwd, Port)) + return NULL; + + // Send the source command to MySQL + if (Myc.ExecSQL(g, Tabsrc, &w) == RC_FX) { + Myc.Close(); + return NULL; + } // endif Exec + + // We must have a storage query to get pivot column values + Qryp = Myc.GetResult(g); + Myc.Close(); + Tqrp = new(g) TDBQRS(Qryp); + Tqrp->OpenDB(g); + + if (MakePivotColumns(g) < 0) + return NULL; + + return Qryp; + } // end of GetSourceTable + +/***********************************************************************/ +/* Allocate PIVOT columns description block. */ +/***********************************************************************/ +int TDBPIVOT::MakePivotColumns(PGLOBAL g) + { + if (Mult < 0) { + int ndif, n = 0, nblin = Qryp->Nblin; + PVAL valp; + PCOL cp; + PSRCCOL colp; + PFNCCOL cfnp; + + // Allocate all the source columns + Tqrp->ColDB(g, NULL, 0); + Columns = NULL; // Discard dummy columns blocks + + for (cp = Tqrp->GetColumns(); cp; cp = cp->GetNext()) { + if (cp->InitValue(g)) + return -1; + + if (!stricmp(cp->GetName(), Picol)) { + Xcolp = (PQRSCOL)cp; + Xresp = Xcolp->GetCrp(); + Rblkp = Xresp->Kdata; + } else if (!stricmp(cp->GetName(), Fncol)) { + Fcolp = (PQRSCOL)cp; + } else + if ((colp = new(g) SRCCOL(cp, this, ++n))->Init(g, this)) + return -1; + + } // endfor cp + + if (!Xcolp) { + sprintf(g->Message, MSG(COL_ISNOT_TABLE), + Picol, Tabname ? Tabname : "TabSrc"); + return -1; + } else if (!Fcolp) { + sprintf(g->Message, MSG(COL_ISNOT_TABLE), + Fncol, Tabname ? Tabname : "TabSrc"); + return -1; + } // endif Fcolp + + // Before calling sort, initialize all + Index.Size = nblin * sizeof(int); + Index.Sub = TRUE; // Should be small enough + + if (!PlgDBalloc(g, NULL, Index)) + return -1; + + Offset.Size = (nblin + 1) * sizeof(int); + Offset.Sub = TRUE; // Should be small enough + + if (!PlgDBalloc(g, NULL, Offset)) + return -2; + + ndif = Qsort(g, nblin); + + if (ndif < 0) { // error + return -3; + } else + Ncol = ndif; + + // Now make the functional columns + for (int i = 0; i < Ncol; i++) { + // Allocate the Value used to retieve column names + if (!(valp = AllocateValue(g, Xcolp->GetResultType(), + Xcolp->GetLengthEx(), Xcolp->GetPrecision(), + Xcolp->GetDomain(), Xcolp->GetTo_Tdb()->GetCat()))) + return -4; + + // Get the value that will be the generated column name + valp->SetValue_pvblk(Rblkp, Pex[Pof[i]]); + + // Copy the functional column with new Name and new Value + cfnp = (PFNCCOL)new(g) FNCCOL(Fcolp, this); + + // Initialize the generated column + if (cfnp->InitColumn(g, valp)) + return -5; + + } // endfor i + + // Fields must be updated for ha_connect +// if (UpdateTableFields(g, n + Ncol)) +// return -6; + + // This should be refined later + Mult = nblin; + } // endif Mult + + return Mult; + } // end of MakePivotColumns + +#if 0 +/***********************************************************************/ +/* Update fields in the MySQL table structure */ +/* Note: this does not work. Indeed the new rows are correctly made */ +/* but the final result still specify the unmodified table and the */ +/* returned table only contains the original column values. */ +/* In addition, a new query on the table, when it is into the cache, */ +/* specifies all the new columns and fails because they do not belong */ +/* to the original table. */ +/***********************************************************************/ +bool TDBPIVOT::UpdateTableFields(PGLOBAL g, int n) + { + uchar *trec, *srec, *tptr, *sptr; + int i = 0, k = 0; + uint len; + uint32 nmp, lwm; + size_t buffsize; + PCOL colp; + PHC hc = ((MYCAT*)((PIVOTDEF*)To_Def)->Cat)->GetHandler(); + TABLE *table = hc->GetTable(); + st_mem_root *tmr = &table->mem_root; + st_mem_root *smr = &table->s->mem_root; + Field* *field; + Field *fp, *tfncp, *sfncp; + Field* *ntf; + Field* *nsf; +//my_bitmap_map *org_bitmap; + const MY_BITMAP *map; + + // When sorting read_set selects all columns, so we use def_read_set + map= (const MY_BITMAP *)&table->def_read_set; + + // Find the function field + for (field= table->field; *field; field++) { + fp= *field; + + if (bitmap_is_set(map, fp->field_index)) + if (!stricmp(fp->field_name, Fncol)) { + tfncp = fp; + break; + } // endif Name + + } // endfor field + + for (field= table->s->field; *field; field++) { + fp= *field; + + if (bitmap_is_set(map, fp->field_index)) + if (!stricmp(fp->field_name, Fncol)) { + sfncp = fp; + break; + } // endif Name + + } // endfor field + + // Calculate the new buffer size + len = tfncp->max_data_length(); + buffsize = table->s->rec_buff_length + len * Ncol; + + // Allocate the new record space + if (!(tptr = trec = (uchar*)alloc_root(tmr, 2 * buffsize))) + return TRUE; + + if (!(sptr = srec = (uchar*)alloc_root(smr, 2 * buffsize))) + return TRUE; + + + // Allocate the array of all new table field pointers + if (!(ntf = (Field**)alloc_root(tmr, (uint)((n+1) * sizeof(Field*))))) + return TRUE; + + // Allocate the array of all new table share field pointers + if (!(nsf = (Field**)alloc_root(smr, (uint)((n+1) * sizeof(Field*))))) + return TRUE; + + // First fields are the the ones of the source columns + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_SRC) { + for (field= table->field; *field; field++) { + fp= *field; + + if (bitmap_is_set(map, fp->field_index)) + if (!stricmp(colp->GetName(), fp->field_name)) { + ntf[i] = fp; + fp->field_index = i++; + fp->ptr = tptr; + tptr += fp->max_data_length(); + break; + } // endif Name + + } // endfor field + + for (field= table->s->field; *field; field++) { + fp= *field; + + if (bitmap_is_set(map, fp->field_index)) + if (!stricmp(colp->GetName(), fp->field_name)) { + nsf[k] = fp; + fp->field_index = k++; + fp->ptr = srec; + srec += fp->max_data_length(); + break; + } // endif Name + + } // endfor field + + } // endif AmType + + // Now add the pivot generated columns + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_FNC) { + if ((fp = (Field*)memdup_root(tmr, (char*)tfncp, tfncp->size_of()))) { + ntf[i] = fp; + fp->ptr = tptr; + fp->field_name = colp->GetName(); + fp->field_index = i++; + fp->vcol_info = NULL; + fp->stored_in_db = TRUE; + tptr += len; + } else + return TRUE; + + if ((fp = (Field*)memdup_root(smr, (char*)sfncp, sfncp->size_of()))) { + nsf[i] = fp; + fp->ptr = sptr; + fp->field_name = colp->GetName(); + fp->field_index = k++; + fp->vcol_info = NULL; + fp->stored_in_db = TRUE; + sptr += len; + } else + return TRUE; + + } // endif AM_FNC + + // Mark end of the list + ntf[i] = NULL; + nsf[k] = NULL; + + // Update the table fields + nmp = (uint32)((1<<i) - 1); + lwm = (uint32)((-1)<<i); + table->field = ntf; + table->used_fields = i; + table->record[0] = trec; + table->record[1] = trec + buffsize; + *table->def_read_set.bitmap = nmp; + *table->def_read_set.last_word_ptr = nmp; + table->def_read_set.last_word_mask = lwm; + table->def_read_set.n_bits = i; + *table->read_set->bitmap = nmp; + *table->read_set->last_word_ptr = nmp; + table->read_set->last_word_mask = lwm; + table->read_set->n_bits = i; + table->write_set->n_bits = i; + *table->vcol_set->bitmap = 0; + table->vcol_set->n_bits = i; + + // and the share fields + table->s->field = nsf; + table->s->reclength = sptr - srec; + table->s->stored_rec_length = sptr - srec; + table->s->fields = k; + table->s->stored_fields = k; + table->s->rec_buff_length = buffsize; +//table->s->varchar_fields = ???; +//table->s->db_record_offset = ???; +//table->s->null_field_first = ???; + return FALSE; + } // end of UpdateTableFields +#endif // 0 + +/***********************************************************************/ +/* Allocate source column description block. */ +/***********************************************************************/ +PCOL TDBPIVOT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PCOL colp = NULL; + +//if (stricmp(cdp->GetName(), Picol) && stricmp(cdp->GetName(), Fncol)) { + colp = new(g) COLBLK(cdp, this, n); + +// if (((PSRCCOL)colp)->Init(g, this)) +// return NULL; + +//} else { +// sprintf(g->Message, MSG(NO_MORE_COL), cdp->GetName()); +// return NULL; +//} // endif Name + + if (cprec) { + colp->SetNext(cprec->GetNext()); + cprec->SetNext(colp); + } else { + colp->SetNext(Columns); + Columns = colp; + } // endif cprec + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* PIVOT GetMaxSize: returns the maximum number of rows in the table. */ +/***********************************************************************/ +int TDBPIVOT::GetMaxSize(PGLOBAL g) + { +#if 0 + if (MaxSize < 0) + MaxSize = MakePivotColumns(g); + + return MaxSize; +#endif // 0 + return 0; + } // end of GetMaxSize + +/***********************************************************************/ +/* In this sample, ROWID will be the (virtual) row number, */ +/* while ROWNUM will be the occurence rank in the multiple column. */ +/***********************************************************************/ +int TDBPIVOT::RowNumber(PGLOBAL g, bool b) + { + return (b) ? M : N; + } // end of RowNumber + +/***********************************************************************/ +/* PIVOT Access Method opening routine. */ +/***********************************************************************/ +bool TDBPIVOT::OpenDB(PGLOBAL g) + { +//PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + N = M = 0; + RowFlag = 0; + FileStatus = 0; + return FALSE; + } // endif use + + /*********************************************************************/ + /* Do it here if not done yet (should not be the case). */ + /*********************************************************************/ +//if (MakePivotColumns(g) < 0) + if (!(Qryp = GetSourceTable(g))) + return TRUE; + + if (Mode != MODE_READ) { + /*******************************************************************/ + /* Currently PIVOT tables cannot be modified. */ + /*******************************************************************/ + sprintf(g->Message, MSG(TABLE_READ_ONLY), "PIVOT"); + return TRUE; + } // endif Mode + + if (To_Key_Col || To_Kindex) { + /*******************************************************************/ + /* Direct access of PIVOT tables is not implemented yet. */ + /*******************************************************************/ + strcpy(g->Message, MSG(NO_PIV_DIR_ACC)); + return TRUE; + } // endif To_Key_Col + + return FALSE; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for PIVOT access method. */ +/***********************************************************************/ +int TDBPIVOT::ReadDB(PGLOBAL g) + { + int rc = RC_OK; + bool newrow = FALSE; + PCOL colp; + PVAL vp1, vp2; + + if (FileStatus == 2) + return RC_EF; + + if (FileStatus) + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_SRC) + ((PSRCCOL)colp)->SetColumn(); + + // New row, reset all function column values + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_FNC) + colp->GetValue()->Reset(); + + /*********************************************************************/ + /* Now start the multi reading process. */ + /*********************************************************************/ + do { + if (RowFlag != 1) { + if ((rc = Tqrp->ReadDB(g)) != RC_OK) { + if (FileStatus && rc == RC_EF) { + // A prepared row remains to be sent + FileStatus = 2; + rc = RC_OK; + } // endif FileStatus + + break; + } // endif rc + + for (colp = Tqrp->GetColumns(); colp; colp = colp->GetNext()) + colp->ReadColumn(g); + + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_SRC) + if (FileStatus) { + if (((PSRCCOL)colp)->CompareColumn()) + newrow = (RowFlag) ? TRUE : FALSE; + + } else + ((PSRCCOL)colp)->SetColumn(); + + FileStatus = 1; + } // endif RowFlag + + if (newrow) { + RowFlag = 1; + break; + } else + RowFlag = 2; + + vp1 = Xcolp->GetValue(); + + // Look for the column having this header + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_FNC) { + vp2 = ((PFNCCOL)colp)->Hval; + + if (!vp1->CompareValue(vp2)) + break; + + } // endif AmType + + if (!colp) { + strcpy(g->Message, MSG(NO_MATCH_COL)); + return RC_FX; + } // endif colp + + // Set the value of the matching column from the fonction value + colp->GetValue()->SetValue_pval(Fcolp->GetValue()); + } while (RowFlag == 2); + + N++; + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for PIVOT access methods. */ +/***********************************************************************/ +int TDBPIVOT::WriteDB(PGLOBAL g) + { + sprintf(g->Message, MSG(TABLE_READ_ONLY), "PIVOT"); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for PIVOT access methods. */ +/***********************************************************************/ +int TDBPIVOT::DeleteDB(PGLOBAL g, int irc) + { + sprintf(g->Message, MSG(NO_TABLE_DEL), "PIVOT"); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for PIVOT access method. */ +/***********************************************************************/ +void TDBPIVOT::CloseDB(PGLOBAL g) + { +//Tdbp->CloseDB(g); + } // end of CloseDB + +/***********************************************************************/ +/* TDBPIVOT: Compare routine for sorting pivot column values. */ +/***********************************************************************/ +int TDBPIVOT::Qcompare(int *i1, int *i2) + { + // TODO: the actual comparison between pivot column result values. + return Rblkp->CompVal(*i1, *i2); + } // end of Qcompare + +// ------------------------ FNCCOL functions ---------------------------- + +/***********************************************************************/ +/* FNCCOL public constructor. */ +/***********************************************************************/ +FNCCOL::FNCCOL(PCOL col1, PTDBPIVOT tdbp) + : COLBLK(col1, tdbp) + { + Value = NULL; // We'll get a new one later + Hval = NULL; // The unconverted header value + } // end of FNCCOL constructor + +/***********************************************************************/ +/* FNCCOL initialization function. */ +/***********************************************************************/ +bool FNCCOL::InitColumn(PGLOBAL g, PVAL valp) +{ + char *p, buf[128]; + int len; + + // Must have its own value block + if (InitValue(g)) + return TRUE; + + // Convert header value to a null terminated character string + Hval = valp; + p = Hval->GetCharString(buf); + len = strlen(p) + 1; + + if (len > (signed)sizeof(buf)) { + strcpy(g->Message, MSG(COLNAM_TOO_LONG)); + return TRUE; + } // endif buf + + // Set the name of the functional pivot column + Name = (PSZ)PlugSubAlloc(g, NULL, len); + strcpy(Name, p); + AddStatus(BUF_READ); // All is done here + return FALSE; +} // end of InitColumn + +// ------------------------ SRCCOL functions ---------------------------- + +#if 0 +/***********************************************************************/ +/* SRCCOL public constructor. */ +/***********************************************************************/ +SRCCOL::SRCCOL(PCOLDEF cdp, PTDBPIVOT tdbp, int n) + : COLBLK(cdp, tdbp, n) + { + // Set additional SRC access method information for column. + Colp = NULL; + Cnval = NULL; + } // end of SRCCOL constructor +#endif // 0 + +/***********************************************************************/ +/* SRCCOL public constructor. */ +/***********************************************************************/ +SRCCOL::SRCCOL(PCOL cp, PTDBPIVOT tdbp, int n) + : COLBLK(cp, tdbp) + { + Index = n; + + // Set additional SRC access method information for column. + Colp = (PQRSCOL)cp; + + // Don't share Value with the source column so we can compare + Cnval = Value = NULL; + } // end of SRCCOL constructor + +#if 0 +/***********************************************************************/ +/* SRCCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +SRCCOL::SRCCOL(SRCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Colp = col1->Colp; + } // end of SRCCOL copy constructor +#endif // 0 + +/***********************************************************************/ +/* Initialize the column as pointing to the source column. */ +/***********************************************************************/ +bool SRCCOL::Init(PGLOBAL g, PTDBPIVOT tdbp) + { + // Currently we ignore the type of the create table column + bool conv = FALSE; + +#if 0 + if (!Colp) { + // Column was defined in the Create Table statement + if (!(Colp = tdbp->ColDB(g, Name, 0))) + return TRUE; + + // We can have a conversion problem converting from numeric to + // character because GetCharValue does not exist for numeric types. + conv = (IsTypeChar(Buf_Type) && IsTypeNum(Colp->GetResultType())); + } else +#endif // 0 + conv = FALSE; + + if (InitValue(g)) + return TRUE; + +//if (conv) +// Cnval = AllocateValue(g, Colp->GetValue()); +//else + Cnval = Value; + + AddStatus(BUF_READ); // All is done here + return FALSE; + } // end of SRCCOL constructor + +/***********************************************************************/ +/* SetColumn: have the column value set from the source column. */ +/***********************************************************************/ +void SRCCOL::SetColumn(void) + { +#if defined(_DEBUG) + Cnval->SetValue_pval(Colp->GetValue(), TRUE); +#else + Cnval->SetValue_pval(Colp->GetValue()); +#endif // _DEBUG + + if (Value != Cnval) + // Convert value + Value->SetValue_pval(Cnval); + + } // end of SetColumn + +/***********************************************************************/ +/* SetColumn: Compare column value with source column value. */ +/***********************************************************************/ +bool SRCCOL::CompareColumn(void) + { + // Compare the unconverted values + return Cnval->CompareValue(Colp->GetValue()); + } // end of CompareColumn + + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBQRS class. */ +/***********************************************************************/ +TDBQRS::TDBQRS(PTDBQRS tdbp) : TDBASE(tdbp) + { + Qrp = tdbp->Qrp; + CurPos = tdbp->CurPos; + } // end of TDBQRS copy constructor + +// Method +PTDB TDBQRS::CopyOne(PTABS t) + { + PTDB tp; + PQRSCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBQRS(this); + + for (cp1 = (PQRSCOL)Columns; cp1; cp1 = (PQRSCOL)cp1->GetNext()) { + cp2 = new(g) QRSCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +#if 0 // The TDBASE functions return NULL when To_Def is NULL +/***********************************************************************/ +/* Return the pointer on the DB catalog this table belongs to. */ +/***********************************************************************/ +PCATLG TDBQRS::GetCat(void) + { + // To_Def is null for QRYRES tables + return NULL; + } // end of GetCat + +/***********************************************************************/ +/* Return the datapath of the DB this table belongs to. */ +/***********************************************************************/ +PSZ TDBQRS::GetPath(void) + { + // To_Def is null for QRYRES tables + return NULL; + } // end of GetPath +#endif // 0 + +/***********************************************************************/ +/* Initialize QRS column description block construction. */ +/* name is used to call columns by name. */ +/* num is used by LNA to construct columns by index number. */ +/* Note: name=Null and num=0 for constructing all columns (select *) */ +/***********************************************************************/ +PCOL TDBQRS::ColDB(PGLOBAL g, PSZ name, int num) + { + int i; + PCOLRES crp; + PCOL cp, colp = NULL, cprec = NULL; + + if (trace) + htrc("QRS ColDB: colname=%s tabname=%s num=%d\n", + SVP(name), Name, num); + + for (crp = Qrp->Colresp, i = 1; crp; crp = crp->Next, i++) + if ((!name && !num) || + (name && !stricmp(crp->Name, name)) || num == i) { + // Check for existence of desired column + // Also find where to insert the new block + for (cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetIndex() < i) + cprec = cp; + else if (cp->GetIndex() == i) + break; + + if (trace) { + if (cp) + htrc("cp(%d).Name=%s cp=%p\n", i, cp->GetName(), cp); + else + htrc("cp(%d) cp=%p\n", i, cp); + } // endif trace + + // Now take care of Column Description Block + if (cp) + colp = cp; + else + colp = new(g) QRSCOL(g, crp, this, cprec, i); + + if (name || num) + break; + else + cprec = colp; + + } // endif Name + + return (colp); + } // end of ColDB + +/***********************************************************************/ +/* QRS GetMaxSize: returns maximum table size in number of lines. */ +/***********************************************************************/ +int TDBQRS::GetMaxSize(PGLOBAL g) + { + MaxSize = Qrp->Maxsize; + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* RowNumber: returns the current row ordinal number. */ +/***********************************************************************/ +int TDBQRS::RowNumber(PGLOBAL g, BOOL b) + { + return (CurPos + 1); + } // end of RowNumber + +/***********************************************************************/ +/* QRS Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBQRS::OpenDB(PGLOBAL g) + { + if (trace) + htrc("QRS OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n", + this, Tdb_No, Use, To_Key_Col, Mode); + + if (Mode != MODE_READ) { + sprintf(g->Message, MSG(BAD_QUERY_OPEN), Mode); + return TRUE; + } // endif Mode + + CurPos = -1; + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + if (To_Kindex) + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + return FALSE; + } // endif use + + /*********************************************************************/ + /* Open (retrieve data from) the query if not already open. */ + /*********************************************************************/ + Use = USE_OPEN; // Do it now in case we are recursively called + + return FALSE; + } // end of OpenDB + +/***********************************************************************/ +/* GetRecpos: returns current position of next sequential read. */ +/***********************************************************************/ +int TDBQRS::GetRecpos(void) + { + return (CurPos); + } // end of GetRecpos + +/***********************************************************************/ +/* ReadDB: Data Base read routine for QRS access method. */ +/***********************************************************************/ +int TDBQRS::ReadDB(PGLOBAL g) + { + int rc = RC_OK; + + if (trace) + htrc("QRS ReadDB: R%d CurPos=%d key=%p link=%p Kindex=%p\n", + GetTdb_No(), CurPos, To_Key_Col, To_Link, To_Kindex); + + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + rc = RC_EF; + break; + case -2: // No match for join + rc = RC_NF; + break; + case -3: // Same record as last non null one + rc = RC_OK; + break; + default: + /***************************************************************/ + /* Set the file position according to record to read. */ + /***************************************************************/ + CurPos = recpos; + } // endswitch recpos + + if (trace) + htrc("Position is now %d\n", CurPos); + + } else + /*******************************************************************/ + /* !To_Kindex ---> sequential reading */ + /*******************************************************************/ + rc = (++CurPos < Qrp->Nblin) ? RC_OK : RC_EF; + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Dummy WriteDB: just send back an error return. */ +/***********************************************************************/ +int TDBQRS::WriteDB(PGLOBAL g) + { + strcpy(g->Message, MSG(QRY_READ_ONLY)); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Dummy DeleteDB routine, just returns an error code. */ +/***********************************************************************/ +int TDBQRS::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(NO_QRY_DELETE)); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for QRS access method. */ +/***********************************************************************/ +void TDBQRS::CloseDB(PGLOBAL g) + { + if (To_Kindex) { + To_Kindex->Close(); + To_Kindex = NULL; + } // endif + + if (trace) + htrc("Qryres CloseDB"); + +//Qryp->Sqlp->CloseDB(); + } // end of CloseDB + +// ------------------------ QRSCOL functions ---------------------------- + +/***********************************************************************/ +/* QRSCOL public constructor. */ +/***********************************************************************/ +QRSCOL::QRSCOL(PGLOBAL g, PCOLRES crp, PTDB tdbp, PCOL cprec, int i) + : COLBLK(NULL, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional QRS access method information for column. + Crp = crp; + Name = Crp->Name; + Long = Crp->Clen; + Buf_Type = crp->Type; + strcpy(Format.Type, GetFormatType(Buf_Type)); + Format.Length = (SHORT)Long; + Format.Prec = (SHORT)Crp->Prec; + + if (trace) { + htrc("Making new QRSCOL C%d %s at %p\n", Index, Name, this); + htrc(" BufType=%d Long=%d length=%d clen=%d\n", + Buf_Type, Long, Format.Length, Crp->Clen); + } // endif trace + + } // end of QRSCOL constructor + +/***********************************************************************/ +/* QRSCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +QRSCOL::QRSCOL(QRSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Crp = col1->Crp; + } // end of QRSCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to extract the RESCOL block */ +/* current value and convert it to the column buffer type. */ +/***********************************************************************/ +void QRSCOL::ReadColumn(PGLOBAL g) + { + PTDBQRS tdbp = (PTDBQRS)To_Tdb; + + if (trace) + htrc("QRS RC: col %s R%d type=%d CurPos=%d Len=%d\n", + Name, tdbp->GetTdb_No(), Buf_Type, tdbp->CurPos, Crp->Clen); + + if (Crp->Kdata) + Value->SetValue_pvblk(Crp->Kdata, tdbp->CurPos); + else + Value->Reset(); + + } // end of ReadColumn + +/***********************************************************************/ +/* Make file output of a Dos column descriptor block. */ +/***********************************************************************/ +void QRSCOL::Print(PGLOBAL g, FILE *f, UINT n) + { + COLBLK::Print(g, f, n); + + fprintf(f, " Crp=%p\n", Crp); + } // end of Print + +/* --------------------- End of TabPivot/TabQrs ---------------------- */ diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h new file mode 100644 index 00000000000..fb480b9abbf --- /dev/null +++ b/storage/connect/tabpivot.h @@ -0,0 +1,241 @@ +/************** TabPivot H Declares Source Code File (.H) **************/ +/* Name: TABPIVOT.H Version 1.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2012 */ +/* */ +/* This file contains the PIVOT classes declares. */ +/***********************************************************************/ +typedef class TDBPIVOT *PTDBPIVOT; +typedef class FNCCOL *PFNCCOL; +typedef class SRCCOL *PSRCCOL; +typedef class TDBQRS *PTDBQRS; +typedef class QRSCOL *PQRSCOL; + +/* -------------------------- PIVOT classes -------------------------- */ + +/***********************************************************************/ +/* PIVOT: table that provides a view of a source table where the */ +/* pivot column is expended in as many columns as there are distinct */ +/* values in it and containing the function value matching other cols.*/ +/***********************************************************************/ + +/***********************************************************************/ +/* PIVOT table. */ +/***********************************************************************/ +//ass DllExport PIVOTDEF : public TABDEF {/* Logical table description */ +class PIVOTDEF : public TABDEF { /* Logical table description */ + friend class TDBPIVOT; + public: + // Constructor + PIVOTDEF(void) {Pseudo = 3; + Tabname = Tabsrc = Picol = Fncol = Function = NULL;} + + // Implementation + virtual const char *GetType(void) {return "PIVOT";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + + protected: + // Members + char *Host; /* Host machine to use */ + char *User; /* User logon info */ + char *Pwd; /* Password logon info */ + char *DB; /* Database to be used by server */ + char *Tabname; /* Name of source table */ + char *Tabsrc; /* The source table SQL description */ + char *Picol; /* The pivot column */ + char *Fncol; /* The function column */ + char *Function; /* The function applying to group by */ + bool GBdone; /* True if tabname as group by format */ + int Port; /* MySQL port number */ + }; // end of PIVOTDEF + +/***********************************************************************/ +/* This is the class declaration for the PIVOT table. */ +/***********************************************************************/ +//ass DllExport TDBPIVOT : public TDBASE, public CSORT { +class TDBPIVOT : public TDBASE, public CSORT { + friend class FNCCOL; + friend class SRCCOL; + public: + // Constructor + TDBPIVOT(PPIVOTDEF tdp); +//TDBPIVOT(PTDBPIVOT tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_PIVOT;} +//virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBPIVOT(this);} +// void SetTdbp(PTDB tdbp) {Tdbp = tdbp;} + + // Methods +//virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void) {return N;} + virtual void ResetDB(void) {N = 0;} + virtual int RowNumber(PGLOBAL g, bool b = FALSE); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + // The sorting function + virtual int Qcompare(int *, int *); + + protected: + PQRYRES GetSourceTable(PGLOBAL g); + int MakePivotColumns(PGLOBAL g); + bool UpdateTableFields(PGLOBAL g, int n); + + // Members + MYSQLC Myc; // MySQL connection class + PTDBQRS Tqrp; // To the source table result + char *Host; // Host machine to use + char *User; // User logon info + char *Pwd; // Password logon info + char *Database; // Database to be used by server + PQRYRES Qryp; // Points to Query result block + char *Tabname; // Name of source table + char *Tabsrc; // SQL of source table + char *Picol; // Pivot column name + char *Fncol; // Function column name + char *Function; // The function applying to group by + PQRSCOL Fcolp; // To the function column in source + PQRSCOL Xcolp; // To the pivot column in source + PCOLRES Xresp; // To the pivot result column +//PCOLRES To_Sort; // Saved Qryp To_Sort pointer + PVBLK Rblkp; // The value block of the pivot column + bool GBdone; // True when subtable is "Group by" + int Mult; // Multiplication factor + int Ncol; // The number of generated columns + int N; // The current table index + int M; // The occurence rank + int Port; // MySQL port number + BYTE FileStatus; // 0: First 1: Rows 2: End-of-File + BYTE RowFlag; // 0: Ok, 1: Same, 2: Skip + }; // end of class TDBPIVOT + +/***********************************************************************/ +/* Class FNCCOL: for the multiple generated column. */ +/***********************************************************************/ +class FNCCOL : public COLBLK { + friend class TDBPIVOT; + public: + // Constructor + FNCCOL(PCOL colp, PTDBPIVOT tdbp); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_FNC;} + + // Methods + virtual void Reset(void) {} + bool InitColumn(PGLOBAL g, PVAL valp); + + protected: + // Member + PVAL Hval; // The original value used to generate the header + }; // end of class FNCCOL + +/***********************************************************************/ +/* Class SRCCOL: for other source columns. */ +/***********************************************************************/ +class SRCCOL : public COLBLK { + friend class TDBPIVOT; + public: + // Constructors +//SRCCOL(PCOLDEF cdp, PTDBPIVOT tdbp, int n); + SRCCOL(PCOL cp, PTDBPIVOT tdbp, int n); +//SRCCOL(SRCCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_SRC;} + + // Methods + virtual void Reset(void) {} + void SetColumn(void); + bool Init(PGLOBAL g, PTDBPIVOT tdbp); + bool CompareColumn(void); + + protected: + // Default constructor not to be used + SRCCOL(void) {} + + // Members + PQRSCOL Colp; + PVAL Cnval; + }; // end of class SRCCOL + +/***********************************************************************/ +/* TDBQRS: This is the Access Method class declaration for the Query */ +/* Result stored in memory in the current work area (volatil). */ +/***********************************************************************/ +class DllExport TDBQRS : public TDBASE { + friend class QRSCOL; + public: + // Constructor + TDBQRS(PQRYRES qrp) : TDBASE() {Qrp = qrp; CurPos = 0;} + TDBQRS(PTDBQRS tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_QRS;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBQRS(this);} + PQRYRES GetQrp(void) {return Qrp;} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int RowNumber(PGLOBAL g, BOOL b = FALSE); + virtual int GetRecpos(void); +//virtual PCATLG GetCat(void); +//virtual PSZ GetPath(void); + virtual int GetBadLines(void) {return Qrp->BadLines;} + + // Database routines + virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + private: + TDBQRS(void) : TDBASE() {} // Standard constructor not to be used + + protected: + // Members + PQRYRES Qrp; // Points to Query Result block + int CurPos; // Current line position + }; // end of class TDBQRS + +/***********************************************************************/ +/* Class QRSCOL: QRS access method column descriptor. */ +/***********************************************************************/ +class DllExport QRSCOL : public COLBLK { + friend class TDBQRS; + public: + // Constructors + QRSCOL(PGLOBAL g, PCOLRES crp, PTDB tdbp, PCOL cprec, int i); + QRSCOL(QRSCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_QRS;} + PCOLRES GetCrp(void) {return Crp;} + void *GetQrsData(void) {return Crp->Kdata;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void Print(PGLOBAL g, FILE *, UINT); + + protected: + QRSCOL(void) {} // Default constructor not to be used + + // Members + PCOLRES Crp; + }; // end of class QRSCOL + diff --git a/storage/connect/tabsys.cpp b/storage/connect/tabsys.cpp new file mode 100644 index 00000000000..9f933e4e2b8 --- /dev/null +++ b/storage/connect/tabsys.cpp @@ -0,0 +1,891 @@ +/************* TabSys C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABSYS */ +/* ------------- */ +/* Version 2.2 */ +/* */ +/* Author Olivier BERTRAND 2004-2013 */ +/* */ +/* This program are the INI/CFG tables classes. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif // __BORLANDC__ +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <errno.h> +#include <unistd.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "reldef.h" +//#include "xobject.h" +#include "filamtxt.h" +#include "tabdos.h" +#include "tabsys.h" +#include "tabmul.h" +#if defined(UNIX) +#include "osutil.h" +#endif // UNIX + +#define CSZ 36 // Column section name length +#define CDZ 256 // Column definition length + +#if !defined(WIN32) +#define GetPrivateProfileSectionNames(S,L,I) \ + GetPrivateProfileString(NULL,NULL,"",S,L,I) +#endif // !WIN32 + +extern "C" int trace; + +/* -------------- Implementation of the INI classes ------------------ */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +INIDEF::INIDEF(void) + { + Pseudo = 3; + Fn = NULL; + Xname = NULL; + Subtype = '?'; + Layout = '?'; + Ln = 0; + } // end of INIDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool INIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char buf[8], ds[2]; + void *memp = Cat->GetDescp(); + + if (!stricmp(am, "SYS")) + strcpy(ds, "T"); // SYS tables default to T(able) + else + strcpy(ds, "I"); // INI tables default to I(ni) + + Fn = Cat->GetStringCatInfo(g, "Filename", NULL); + Cat->GetCharCatInfo("Subtype", ds, buf, sizeof(buf)); + Subtype = toupper(*buf); + Cat->GetCharCatInfo("Layout", "C", buf, sizeof(buf)); + Layout = toupper(*buf); + + switch (Subtype) { +#if 0 + case 'C': + case 'T': + // Restricted table + Xname = Cat->GetStringCatInfo(g, "Name", "?"); + + if (!strcmp(Xname, "?")) + Xname = NULL; + + if (*Fn == '?') + Fn = Cat->GetStringCatInfo(g, "Database", "?"); + + if (*Fn != '?') { + char *p = (char*)PlugSubAlloc(g, memp, _MAX_PATH); + + if (!PlgSetXdbPath(g, Fn, NULL, p, _MAX_PATH, NULL, 0)) + Fn = p; + + } else + Fn = Cat->GetDescFile(); + + Ln = GetIniSize("Database", "Tabsize", "2K", Fn); + break; +#endif // 0 + case 'I': + if (*Fn != '?') { + char *p = (char*)PlugSubAlloc(g, memp, _MAX_PATH); + + PlugSetPath(p, Fn, GetPath()); + Fn = p; + } else { + strcpy(g->Message, MSG(MISSING_FNAME)); + return true; + } // endif Fn + + Ln = Cat->GetSizeCatInfo("Secsize", "8K"); + break; + default: + sprintf(g->Message, MSG(INV_SUBTYPE), buf); + return true; + } // endswitch Subtype + + Desc = Fn; + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB INIDEF::GetTable(PGLOBAL g, MODE m) + { + PTDBASE tdbp; + + switch (Subtype) { + case 'I': + if (Layout == 'C') + tdbp = new(g) TDBINI(this); + else + tdbp = new(g) TDBXIN(this); + + break; + default: + return NULL; + } // endswitch Subtype + + if (Multiple) + tdbp = new(g) TDBMUL(tdbp); // No block optimization yet + + return tdbp; + } // end of GetTable + +/***********************************************************************/ +/* DeleteTableFile: Delete INI table files using platform API. */ +/* SysTable and SysColumn tables are readonly and not erasable. */ +/***********************************************************************/ +bool INIDEF::DeleteTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc; + + // Delete the INI table file if not protected + if (Subtype == 'I' && !IsReadOnly()) { + PlugSetPath(filename, Fn, GetPath()); +#if defined(WIN32) + rc = !DeleteFile(filename); +#else // UNIX + rc = remove(filename); +#endif // UNIX + } else + rc =true; + + return rc; // Return true if error + } // end of DeleteTableFile + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBINI class. */ +/***********************************************************************/ +TDBINI::TDBINI(PINIDEF tdp) : TDBASE(tdp) + { + Ifile = tdp->Fn; + Seclist = NULL; + Section = NULL; + Seclen = tdp->Ln; + N = 0; + } // end of TDBINI constructor + +TDBINI::TDBINI(PTDBINI tdbp) : TDBASE(tdbp) + { + Ifile = tdbp->Ifile; + Seclist = tdbp->Seclist; + Section = tdbp->Section; + Seclen = tdbp->Seclen; + N = tdbp->N; + } // end of TDBINI copy constructor + +// Is this really useful ??? +PTDB TDBINI::CopyOne(PTABS t) + { + PTDB tp; + PINICOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBINI(this); + + for (cp1 = (PINICOL)Columns; cp1; cp1 = (PINICOL)cp1->GetNext()) { + cp2 = new(g) INICOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Get the section list from the INI file. */ +/***********************************************************************/ +char *TDBINI::GetSeclist(PGLOBAL g) + { + if (trace) + htrc("GetSeclist: Seclist=%p\n", Seclist); + + if (!Seclist) { + // Result will be retrieved from the INI file + Seclist = (char*)PlugSubAlloc(g, NULL, Seclen); + GetPrivateProfileSectionNames(Seclist, Seclen, Ifile); + } // endif Seclist + + return Seclist; + } // end of GetSeclist + +/***********************************************************************/ +/* Allocate INI column description block. */ +/***********************************************************************/ +PCOL TDBINI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) INICOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* INI GetMaxSize: returns the number of sections in the INI file. */ +/***********************************************************************/ +int TDBINI::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0 && GetSeclist(g)) { + // Count the number of sections from the section list + char *p; + + for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1)) + MaxSize++; + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* INI Access Method opening routine. */ +/***********************************************************************/ +bool TDBINI::OpenDB(PGLOBAL g) + { + PINICOL colp; + + if (Use == USE_OPEN) { + if (To_Kindex) + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + Section = NULL; + N = 0; + return false; + } // endif use + + /*********************************************************************/ + /* OpenDB: initialize the INI file processing. */ + /*********************************************************************/ + GetSeclist(g); + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Allocate the buffers that will contain key values. */ + /*********************************************************************/ + for (colp = (PINICOL)Columns; colp; colp = (PINICOL)colp->GetNext()) + if (!colp->IsSpecial()) // Not a pseudo column + colp->AllocBuf(g); + + if (trace) + htrc("INI OpenDB: seclist=%s seclen=%d ifile=%s\n", + Seclist, Seclen, Ifile); + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for INI access method. */ +/***********************************************************************/ +int TDBINI::ReadDB(PGLOBAL g) + { + /*********************************************************************/ + /* Now start the pseudo reading process. */ + /*********************************************************************/ + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + return RC_OK; + default: + Section = (char*)recpos; + } // endswitch recpos + + } else { + if (!Section) + Section = Seclist; + else + Section += (strlen(Section) + 1); + + if (trace > 1) + htrc("INI ReadDB: section=%s N=%d\n", Section, N); + + N++; + } // endif To_Kindex + + return (*Section) ? RC_OK : RC_EF; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for INI access methods. */ +/***********************************************************************/ +int TDBINI::WriteDB(PGLOBAL g) + { + // This is to check that section name was given when inserting + if (Mode == MODE_INSERT) + Section = NULL; + + // Nothing else to do because all was done in WriteColumn + return RC_OK; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for INI access methods. */ +/***********************************************************************/ +int TDBINI::DeleteDB(PGLOBAL g, int irc) + { + switch (irc) { + case RC_EF: + break; + case RC_FX: + while (ReadDB(g) == RC_OK) + WritePrivateProfileString(Section, NULL, NULL, Ifile); + + break; + default: + if (!Section) { + strcpy(g->Message, MSG(NO_SECTION_NAME)); + return RC_FX; + } else + WritePrivateProfileString(Section, NULL, NULL, Ifile); + + } // endswitch irc + + return RC_OK; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for INI access methods. */ +/***********************************************************************/ +void TDBINI::CloseDB(PGLOBAL g) + { +#if !defined(WIN32) + PROFILE_Close(Ifile); +#endif // !WIN32 + } // end of CloseDB + +// ------------------------ INICOL functions ---------------------------- + +/***********************************************************************/ +/* INICOL public constructor. */ +/***********************************************************************/ +INICOL::INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional INI access method information for column. + Valbuf = NULL; + Flag = cdp->GetOffset(); + Long = cdp->GetLong(); + To_Val = NULL; + } // end of INICOL constructor + +/***********************************************************************/ +/* INICOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +INICOL::INICOL(INICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Valbuf = col1->Valbuf; + Flag = col1->Flag; + Long = col1->Long; + To_Val = col1->To_Val; + } // end of INICOL copy constructor + +/***********************************************************************/ +/* Allocate a buffer of the proper size. */ +/***********************************************************************/ +void INICOL::AllocBuf(PGLOBAL g) + { + if (!Valbuf) + Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1); + + } // end of AllocBuf + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool INICOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_FLOAT) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetPrecision()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Allocate the internal value buffer + AllocBuf(g); + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the key buffer set */ +/* from the corresponding section, extract from it the key value */ +/* corresponding to this column name and convert it to buffer type. */ +/***********************************************************************/ +void INICOL::ReadColumn(PGLOBAL g) + { + PTDBINI tdbp = (PTDBINI)To_Tdb; + + if (trace > 1) + htrc("INI ReadColumn: col %s R%d flag=%d\n", + Name, tdbp->GetTdb_No(), Flag); + + /*********************************************************************/ + /* Get the key value from the INI file. */ + /*********************************************************************/ + switch (Flag) { + case 1: + strncpy(Valbuf, tdbp->Section, Long); // Section name + Valbuf[Long] = '\0'; + break; + default: + GetPrivateProfileString(tdbp->Section, Name, "µ", + Valbuf, Long + 1, tdbp->Ifile); + break; + } // endswitch Flag + + // Missing keys are interpreted as null values + if (!strcmp(Valbuf, "µ")) { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } else + Value->SetValue_psz(Valbuf); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void INICOL::WriteColumn(PGLOBAL g) + { + char *p; + PTDBINI tdbp = (PTDBINI)To_Tdb; + + if (trace > 1) + htrc("INI WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + // Null key are missing keys + if (Value->IsNull()) + return; + + p = Value->GetCharString(Valbuf); + + if (strlen(p) > (unsigned)Long) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); + longjmp(g->jumper[g->jump_level], 31); + } else if (Flag == 1) { + if (tdbp->Mode == MODE_UPDATE) { + strcpy(g->Message, MSG(NO_SEC_UPDATE)); + longjmp(g->jumper[g->jump_level], 31); + } else { + tdbp->Section = p; + return; + } // endif Mode + + } else if (!tdbp->Section) { + strcpy(g->Message, MSG(SEC_NAME_FIRST)); + longjmp(g->jumper[g->jump_level], 31); + } // endif's + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + if (Status) + WritePrivateProfileString(tdbp->Section, Name, p, tdbp->Ifile); + + } // end of WriteColumn + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBXIN class. */ +/***********************************************************************/ +TDBXIN::TDBXIN(PINIDEF tdp) : TDBINI(tdp) + { + Keylist = NULL; + Keycur = NULL; + Keylen = Seclen; + Oldsec = -1; + } // end of TDBXIN constructor + +TDBXIN::TDBXIN(PTDBXIN tdbp) : TDBINI(tdbp) + { + Keylist = tdbp->Keylist; + Keycur = tdbp->Keycur; + Keylen = tdbp->Keylen; + Oldsec = tdbp->Oldsec; + } // end of TDBXIN copy constructor + +// Is this really useful ??? +PTDB TDBXIN::CopyOne(PTABS t) + { + PTDB tp; + PXINCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBXIN(this); + + for (cp1 = (PXINCOL)Columns; cp1; cp1 = (PXINCOL)cp1->GetNext()) { + cp2 = new(g) XINCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Get the key list from the INI file. */ +/***********************************************************************/ +char *TDBXIN::GetKeylist(PGLOBAL g, char *sec) + { + if (!Keylist) + Keylist = (char*)PlugSubAlloc(g, NULL, Keylen); + + GetPrivateProfileString(sec, NULL, "", Keylist, Keylen, Ifile); + return Keylist; + } // end of GetKeylist + +/***********************************************************************/ +/* Allocate XIN column description block. */ +/***********************************************************************/ +PCOL TDBXIN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) XINCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* XIN GetMaxSize: returns the number of sections in the XIN file. */ +/***********************************************************************/ +int TDBXIN::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0 && GetSeclist(g)) { + // Count the number of keys from the section list + char *p, *k; + + for (MaxSize = 0, p = Seclist; *p; p += (strlen(p) + 1)) + for (k = GetKeylist(g, p); *k; k += (strlen(k) + 1)) + MaxSize++; + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Record position is Section+Key. */ +/***********************************************************************/ +int TDBXIN::GetRecpos(void) + { + union { + short X[2]; // Section and Key offsets + int Xpos; // File position + }; // end of union + + X[0] = (short)(Section - Seclist); + X[1] = (short)(Keycur - Keylist); + return Xpos; + } // end of GetRecpos + +/***********************************************************************/ +/* Record position is Section+Key. */ +/***********************************************************************/ +bool TDBXIN::SetRecpos(PGLOBAL g, int recpos) + { + union { + short X[2]; // Section and Key offsets + int Xpos; // File position + }; // end of union + + Xpos = recpos; + + if (X[0] != Oldsec) { + Section = Seclist + X[0]; + Keycur = GetKeylist(g, Section) + X[1]; + Oldsec = X[0]; + } else + Keycur = Keylist + X[1]; + + return false; + } // end of SetRecpos + +/***********************************************************************/ +/* XIN Access Method opening routine. */ +/***********************************************************************/ +bool TDBXIN::OpenDB(PGLOBAL g) + { + Oldsec = -1; // To replace the table at its beginning + return TDBINI::OpenDB(g); + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for XIN access method. */ +/***********************************************************************/ +int TDBXIN::ReadDB(PGLOBAL g) + { + /*********************************************************************/ + /* Now start the pseudo reading process. */ + /*********************************************************************/ + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + return RC_OK; + default: + SetRecpos(g, recpos); + } // endswitch recpos + + } else { + do { + if (!Keycur || !*Keycur) { + if (!Section) + Section = Seclist; + else + Section += (strlen(Section) + 1); + + if (*Section) + Keycur = GetKeylist(g, Section); + else + return RC_EF; + + } else + Keycur += (strlen(Keycur) + 1); + + } while (!*Keycur); + + N++; + } // endif To_Kindex + + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for XIN access methods. */ +/***********************************************************************/ +int TDBXIN::WriteDB(PGLOBAL g) + { + // To check that section and key names were given when inserting + if (Mode == MODE_INSERT) { + Section = NULL; + Keycur = NULL; + } // endif Mode + + // Nothing else to do because all was done in WriteColumn + return RC_OK; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for XIN access methods. */ +/***********************************************************************/ +int TDBXIN::DeleteDB(PGLOBAL g, int irc) + { + if (irc == RC_EF) { + } else if (irc == RC_FX) { + for (Section = Seclist; *Section; Section += (strlen(Section) + 1)) + WritePrivateProfileString(Section, NULL, NULL, Ifile); + + } else if (Section) { + WritePrivateProfileString(Section, Keycur, NULL, Ifile); + } else { + strcpy(g->Message, MSG(NO_SECTION_NAME)); + return RC_FX; + } // endif's + + return RC_OK; + } // end of DeleteDB + +// ------------------------ XINCOL functions ---------------------------- + +/***********************************************************************/ +/* XINCOL public constructor. */ +/***********************************************************************/ +XINCOL::XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : INICOL(cdp, tdbp, cprec, i, am) + { + } // end of XINCOL constructor + +/***********************************************************************/ +/* XINCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +XINCOL::XINCOL(XINCOL *col1, PTDB tdbp) : INICOL(col1, tdbp) + { + } // end of XINCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the key buffer set */ +/* from the corresponding section, extract from it the key value */ +/* corresponding to this column name and convert it to buffer type. */ +/***********************************************************************/ +void XINCOL::ReadColumn(PGLOBAL g) + { + PTDBXIN tdbp = (PTDBXIN)To_Tdb; + + /*********************************************************************/ + /* Get the key value from the XIN file. */ + /*********************************************************************/ + switch (Flag) { + case 1: + strncpy(Valbuf, tdbp->Section, Long); // Section name + Valbuf[Long] = '\0'; + break; + case 2: + strncpy(Valbuf, tdbp->Keycur, Long); // Key name + Valbuf[Long] = '\0'; + break; + default: + GetPrivateProfileString(tdbp->Section, tdbp->Keycur, "", + Valbuf, Long + 1, tdbp->Ifile); + break; + } // endswitch Flag + + Value->SetValue_psz(Valbuf); + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void XINCOL::WriteColumn(PGLOBAL g) + { + char *p; + PTDBXIN tdbp = (PTDBXIN)To_Tdb; + + if (trace > 1) + htrc("XIN WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + p = Value->GetCharString(Valbuf); + + if (strlen(p) > (unsigned)Long) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); + longjmp(g->jumper[g->jump_level], 31); + } else if (Flag == 1) { + if (tdbp->Mode == MODE_UPDATE) { + strcpy(g->Message, MSG(NO_SEC_UPDATE)); + longjmp(g->jumper[g->jump_level], 31); + } else { + tdbp->Section = p; + return; + } // endif Mode + + } else if (Flag == 2) { + if (tdbp->Mode == MODE_UPDATE) { + strcpy(g->Message, MSG(NO_KEY_UPDATE)); + longjmp(g->jumper[g->jump_level], 31); + } else { + tdbp->Keycur = p; + return; + } // endif Mode + + } else if (!tdbp->Section || !tdbp->Keycur) { + strcpy(g->Message, MSG(SEC_KEY_FIRST)); + longjmp(g->jumper[g->jump_level], 31); + } // endif's + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + if (Status) + WritePrivateProfileString(tdbp->Section, tdbp->Keycur, p, tdbp->Ifile); + + } // end of WriteColumn + +/* ------------------------ End of System ---------------------------- */ + + diff --git a/storage/connect/tabsys.h b/storage/connect/tabsys.h new file mode 100644 index 00000000000..ac8ae05aee9 --- /dev/null +++ b/storage/connect/tabsys.h @@ -0,0 +1,183 @@ +/*************** TabSys H Declares Source Code File (.H) ***************/ +/* Name: TABSYS.H Version 2.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */ +/* */ +/* This file contains the XDB system tables classes declares. */ +/***********************************************************************/ +typedef class INIDEF *PINIDEF; +typedef class TDBINI *PTDBINI; +typedef class INICOL *PINICOL; +typedef class TDBXIN *PTDBXIN; +typedef class XINCOL *PXINCOL; + +/* --------------------------- INI classes --------------------------- */ + +/***********************************************************************/ +/* INI, XDB and XCL tables. */ +/***********************************************************************/ +class DllExport INIDEF : public TABDEF { /* INI table description */ + friend class TDBINI; + friend class TDBXIN; + friend class TDBXTB; + friend class TDBRTB; + friend class TDBXCL; + public: + // Constructor + INIDEF(void); + + // Implementation + virtual const char *GetType(void) {return "INI";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + virtual bool DeleteTableFile(PGLOBAL g); + + protected: + // Members + char *Fn; /* Path/Name of corresponding file */ + char *Xname; /* The eventual table name */ + char Subtype; /* I: INI, T: Table, C: Column */ + char Layout; /* R: Row, C: Column */ + int Ln; /* Length of section list buffer */ + }; // end of INIDEF + +/***********************************************************************/ +/* This is the class declaration for the INI tables. */ +/* These are tables represented by a INI like file. */ +/***********************************************************************/ +class TDBINI : public TDBASE { + friend class INICOL; + public: + // Constructor + TDBINI(PINIDEF tdp); + TDBINI(PTDBINI tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_INI;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBINI(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void) {return N;} + virtual int GetProgCur(void) {return N;} + virtual int GetAffectedRows(void) {return 0;} + virtual PSZ GetFile(PGLOBAL g) {return Ifile;} + virtual void SetFile(PGLOBAL g, PSZ fn) {Ifile = fn;} + virtual void ResetDB(void) {Seclist = Section = NULL; N = 0;} + virtual void ResetSize(void) {MaxSize = -1; Seclist = NULL;} + virtual int RowNumber(PGLOBAL g, bool b = false) {return N;} + char *GetSeclist(PGLOBAL g); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Members + char *Ifile; // The INI file + char *Seclist; // The section list + char *Section; // The current section + int Seclen; // Length of seclist buffer + int N; // The current section index + }; // end of class TDBINI + +/***********************************************************************/ +/* Class INICOL: XDB table access method column descriptor. */ +/***********************************************************************/ +class INICOL : public COLBLK { + public: + // Constructors + INICOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI"); + INICOL(INICOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_INI;} + virtual void SetTo_Val(PVAL valp) {To_Val = valp;} + + // Methods + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + virtual void AllocBuf(PGLOBAL g); + + protected: + // Default constructor not to be used + INICOL(void) {} + + // Members + char *Valbuf; // To the key value buffer + int Flag; // Tells what set in value + int Long; // Buffer length + PVAL To_Val; // To value used for Update/Insert + }; // end of class INICOL + +/* --------------------------- XINI class ---------------------------- */ + +/***********************************************************************/ +/* This is the class declaration for the XINI tables. */ +/* These are tables represented by a INI like file */ +/* having 3 columns Section, Key, and Value. */ +/***********************************************************************/ +class TDBXIN : public TDBINI { + friend class XINCOL; + public: + // Constructor + TDBXIN(PINIDEF tdp); + TDBXIN(PTDBXIN tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_INI;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXIN(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void); + virtual bool SetRecpos(PGLOBAL g, int recpos); + virtual void ResetDB(void) + {Seclist = Section = Keycur = NULL; N = 0; Oldsec = -1;} + char *GetKeylist(PGLOBAL g, char *sec); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + + protected: + // Members + char *Keylist; // The key list + char *Keycur; // The current key + int Keylen; // Length of keylist buffer + short Oldsec; // Last current section + }; // end of class TDBXIN + +/***********************************************************************/ +/* Class XINCOL: XIN table access method column descriptor. */ +/***********************************************************************/ +class XINCOL : public INICOL { + public: + // Constructors + XINCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "INI"); + XINCOL(XINCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + + protected: + // Default constructor not to be used + XINCOL(void) {} + + // Members + }; // end of class XINICOL diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp new file mode 100644 index 00000000000..8a662588dd1 --- /dev/null +++ b/storage/connect/tabtbl.cpp @@ -0,0 +1,816 @@ +/************* TabTbl C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABTBL */ +/* ------------- */ +/* Version 1.4 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to PlugDB Software Development 2008-2013 */ +/* Author: Olivier BERTRAND */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the TDBTBL class DB routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* TABTBL.CPP - Source code */ +/* PLGDBSEM.H - DB application declaration file */ +/* TABDOS.H - TABDOS classes declaration file */ +/* TABTBL.H - TABTBL classes declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant section of system dependant header files. */ +/***********************************************************************/ +//#include "sql_base.h" +#include "my_global.h" +#if defined(WIN32) +#include <stdlib.h> +#include <stdio.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#else +#if defined(UNIX) +#include <fnmatch.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "osutil.h" +#else +//#include <io.h> +#endif +//#include <fcntl.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/***********************************************************************/ +#include "table.h" // MySQL table definitions +#include "global.h" // global declarations +#include "plgdbsem.h" // DB application declarations +#include "reldef.h" // DB definition declares +//#include "filter.h" // FILTER classes dcls +#include "filamtxt.h" +#include "tabcol.h" +#include "tabdos.h" // TDBDOS and DOSCOL class dcls +#include "tabtbl.h" // TDBTBL and TBLCOL classes dcls +#if defined(MYSQL_SUPPORT) +#include "tabmysql.h" +#endif // MYSQL_SUPPORT +#include "ha_connect.h" +#include "mycat.h" // For GetHandler + +extern "C" int trace; + +int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags); + +/* ---------------------------- Class TBLDEF ---------------------------- */ + +/**************************************************************************/ +/* Constructor. */ +/**************************************************************************/ +TBLDEF::TBLDEF(void) + { + To_Tables = NULL; + Ntables = 0; + Pseudo = 3; + } // end of TBLDEF constructor + +/**************************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/**************************************************************************/ +bool TBLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char *tablist, *dbname; + + Desc = "Table list table"; + tablist = Cat->GetStringCatInfo(g, "Tablist", ""); + dbname = Cat->GetStringCatInfo(g, "Database", NULL); + Ntables = 0; + + if (*tablist) { + char *p, *pn, *pdb; + PTBL *ptbl = &To_Tables, tbl; + + for (pdb = tablist; ;) { + if ((p = strchr(pdb, ','))) + *p = 0; + + // Analyze the table name, it has the format: + // [dbname.]tabname + if ((pn = strchr(pdb, '.'))) { + *pn++ = 0; + } else { + pn = pdb; + pdb = dbname; + } // endif p + + // Allocate the TBLIST block for that table + tbl = (PTBL)PlugSubAlloc(g, NULL, sizeof(TBLIST)); + tbl->Next = NULL; + tbl->Name = pn; + tbl->DB = pdb; + + if (trace) + htrc("TBL: Name=%s db=%s\n", tbl->Name, SVP(tbl->DB)); + + // Link the blocks + *ptbl = tbl; + ptbl = &tbl->Next; + Ntables++; + + if (p) + pdb = pn + strlen(pn) + 1; + else + break; + + } // endfor pdb + + Maxerr = Cat->GetIntCatInfo("Maxerr", 0); + Accept = (Cat->GetBoolCatInfo("Accept", 0) != 0); + } // endif fsec || tablist + + return FALSE; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB TBLDEF::GetTable(PGLOBAL g, MODE m) + { + PTDB tdbp; + + /*********************************************************************/ + /* Allocate a TDB of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + tdbp = new(g) TDBTBL(this); + + return tdbp; + } // end of GetTable + +/* ------------------------- Class TDBTBL ---------------------------- */ + +/***********************************************************************/ +/* TDBTBL constructors. */ +/***********************************************************************/ +TDBTBL::TDBTBL(PTBLDEF tdp) : TDBASE(tdp) + { + Tablist = NULL; + CurTable = NULL; + Tdbp = NULL; + Accept = tdp->Accept; + Maxerr = tdp->Maxerr; + Nbf = 0; + Rows = 0; + Crp = 0; +// NTables = 0; +// iTable = 0; + } // end of TDBTBL standard constructor + +/***********************************************************************/ +/* Allocate TBL column description block. */ +/***********************************************************************/ +PCOL TDBTBL::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) TBLCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* InsertSpecialColumn: Put a special column ahead of the column list.*/ +/***********************************************************************/ +PCOL TDBTBL::InsertSpecialColumn(PGLOBAL g, PCOL scp) + { + PCOL colp; + + if (!scp->IsSpecial()) + return NULL; + + if (scp->GetAmType() == TYPE_AM_TABID) + // This special column is handled locally + colp = new((TIDBLK*)scp) TBTBLK(scp->GetValue()); + else // Other special columns are treated normally + colp = scp; + + colp->SetNext(Columns); + Columns = colp; + return colp; + } // end of InsertSpecialColumn + +/***********************************************************************/ +/* Get the PTDB of a table of the list. */ +/***********************************************************************/ +PTDB TDBTBL::GetSubTable(PGLOBAL g, PTBL tblp, PTABLE tabp) + { + char *db, key[256]; + uint k, flags; + PTDB tdbp = NULL; + TABLE_LIST table_list; + TABLE_SHARE *s; + PCATLG cat = To_Def->GetCat(); + PHC hc = ((MYCAT*)cat)->GetHandler(); + THD *thd = (hc->GetTable())->in_use; + + if (!thd) + return NULL; // Should not happen anymore + + if (tblp->DB) + db = tblp->DB; + else + db = (char*)hc->GetDBName(NULL); + + table_list.init_one_table(db, strlen(db), + tblp->Name, strlen(tblp->Name), + NULL, TL_IGNORE); + k = sprintf(key, "%s", db); + k += sprintf(key + ++k, "%s", tblp->Name); + key[++k] = 0; + + if (!(s = alloc_table_share(&table_list, key, ++k))) { + strcpy(g->Message, "Error allocating share\n"); + return NULL; + } // endif s + +// 1 8 16 +//flags = READ_ALL | DONT_OPEN_TABLES | DONT_OPEN_MASTER_REG; +//flags = 25; + flags = 24; + + if (!open_table_def(thd, s, flags)) { +#ifdef DBUG_OFF + if (stricmp(s->db_plugin->name.str, "connect")) { +#else + if (stricmp((*s->db_plugin)->name.str, "connect")) { +#endif +#if defined(MYSQL_SUPPORT) + // Access sub-table via MySQL API + if (!(tdbp= cat->GetTable(g, tabp, MODE_READ, "MYSQL"))) { + sprintf(g->Message, "Cannot access %s.%s", db, tblp->Name); + return NULL; + } // endif Define + + if (tabp->GetQualifier()) + ((PTDBMY)tdbp)->SetDatabase(tabp->GetQualifier()); + +#else // !MYSQL_SUPPORT + sprintf(g->Message, "%s.%s is not a CONNECT table", + db, tblp->Name); + return NULL; +#endif // MYSQL_SUPPORT + } else { + // Sub-table is a CONNECT table + hc->tshp = s; + tdbp = cat->GetTable(g, tabp); + hc->tshp = NULL; + } // endif plugin + + } else + sprintf(g->Message, "Error %d opening share\n", s->error); + + if (trace && tdbp) + htrc("Subtable %s in %s\n", + tblp->Name, SVP(((PTDBASE)tdbp)->GetDef()->GetDB())); + + free_table_share(s); + return tdbp; + } // end of GetSubTable + +/***********************************************************************/ +/* Initializes the table table list. */ +/***********************************************************************/ +bool TDBTBL::InitTableList(PGLOBAL g) + { + char *colname; + int n, colpos; + PTBL tblp; + PTABLE tabp; + PTDB tdbp; + PCOL colp; + PTBLDEF tdp = (PTBLDEF)To_Def; + +// PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath()); + + for (n = 0, tblp = tdp->GetTables(); tblp; tblp = tblp->Next) { + if (TestFil(g, To_Filter, tblp)) { + // Table or named view + tabp = new(g) XTAB(tblp->Name); + tabp->SetQualifier(tblp->DB); + + // Get the table description block of this table + if (!(tdbp = GetSubTable(g, tblp, tabp))) { + if (++Nbf > Maxerr) + return TRUE; // Error return + else + continue; // Skip this table + + } // endif tdbp + + // We must allocate subtable columns before GetMaxSize is called + // because some (PLG, ODBC?) need to have their columns attached. + // Real initialization will be done later. + for (PCOL cp = Columns; cp; cp = cp->GetNext()) + if (!cp->IsSpecial()) { + colname = cp->GetName(); + colpos = ((PTBLCOL)cp)->Colnum; + + // We try first to get the column by name + if (!(colp = tdbp->ColDB(g, colname, 0)) && colpos) + // When unsuccessful, if a column number was specified + // try to get the column by its position in the table + colp = tdbp->ColDB(g, NULL, colpos); + + if (!colp) { + if (!Accept) { + sprintf(g->Message, MSG(NO_MATCHING_COL), + colname, tdbp->GetName()); + return TRUE; // Error return + } // endif !Accept + + } else // this is needed in particular by PLG tables + colp->SetColUse(cp->GetColUse()); + + } // endif !special + + if (Tablist) + Tablist->Link(tabp); + else + Tablist = tabp; + + n++; + } // endif filp + + } // endfor tblp + +//NumTables = n; + To_Filter = NULL; // To avoid doing it several times + return FALSE; + } // end of InitTableList + +/***********************************************************************/ +/* Test the tablename against the pseudo "local" filter. */ +/***********************************************************************/ +bool TDBTBL::TestFil(PGLOBAL g, PFIL filp, PTBL tblp) + { + char *fil, op[8], tn[NAME_LEN]; + bool neg; + + if (!filp) + return TRUE; + else if (strstr(filp, " OR ") || strstr(filp, " AND ")) + return TRUE; // Not handled yet + else + fil = filp + (*filp == '(' ? 1 : 0); + + if (sscanf(fil, "TABID %s", op) != 1) + return TRUE; // ignore invalid filter + + if ((neg = !strcmp(op, "NOT"))) + strcpy(op, "IN"); + + if (!strcmp(op, "=")) { + // Temporarily, filter must be "TABID = 'value'" only + if (sscanf(fil, "TABID = '%[^']'", tn) != 1) + return TRUE; // ignore invalid filter + + return !stricmp(tn, tblp->Name); + } else if (!strcmp(op, "IN")) { + char *p, *tnl = (char*)PlugSubAlloc(g, NULL, strlen(fil) - 10); + int n; + + if (neg) + n = sscanf(fil, "TABID NOT IN (%[^)])", tnl); + else + n = sscanf(fil, "TABID IN (%[^)])", tnl); + + if (n != 1) + return TRUE; // ignore invalid filter + + while (tnl) { + if ((p = strchr(tnl, ','))) + *p++ = 0; + + if (sscanf(tnl, "'%[^']'", tn) != 1) + return TRUE; // ignore invalid filter + else if (!stricmp(tn, tblp->Name)) + return !neg; // Found + + tnl = p; + } // endwhile + + return neg; // Not found + } // endif op + + return TRUE; // invalid operator + } // end of TestFil + +/***********************************************************************/ +/* TBL GetProgMax: get the max value for progress information. */ +/***********************************************************************/ +int TDBTBL::GetProgMax(PGLOBAL g) + { + PTABLE tblp; + int n, pmx = 0; + + if (!Tablist && InitTableList(g)) + return -1; + + for (tblp = Tablist; tblp; tblp = tblp->GetNext()) + if ((n = tblp->GetTo_Tdb()->GetProgMax(g)) > 0) + pmx += n; + + return pmx; + } // end of GetProgMax + +/***********************************************************************/ +/* TBL GetProgCur: get the current value for progress information. */ +/***********************************************************************/ +int TDBTBL::GetProgCur(void) + { + return Crp + Tdbp->GetProgCur(); + } // end of GetProgCur + +#if 0 +/***********************************************************************/ +/* TBL Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/* Can be used on Multiple FIX table only. */ +/***********************************************************************/ +int TDBTBL::Cardinality(PGLOBAL g) + { + if (!g) + return Tdbp->Cardinality(g); + + if (!Tablist && InitTableList(g)) + return -1; + + int n, card = 0; + + for (int i = 0; i < NumFiles; i++) { + Tdbp->SetFile(g, Filenames[i]); + Tdbp->ResetSize(); + + if ((n = Tdbp->Cardinality(g)) < 0) { +// strcpy(g->Message, MSG(BAD_CARDINALITY)); + return -1; + } // endif n + + card += n; + } // endfor i + + return card; + } // end of Cardinality +#endif // 0 + +/***********************************************************************/ +/* Sum up the sizes of all sub-tables. */ +/***********************************************************************/ +int TDBTBL::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + PTABLE tblp; + int mxsz; + + if (!Tablist && InitTableList(g)) + return 0; // Cannot be calculated at this stage + +// if (Use == USE_OPEN) { +// strcpy(g->Message, MSG(MAXSIZE_ERROR)); +// return -1; +// } else + MaxSize = 0; + + for (tblp = Tablist; tblp; tblp = tblp->GetNext()) { + if ((mxsz = tblp->GetTo_Tdb()->GetMaxSize(g)) < 0) { + MaxSize = -1; + return mxsz; + } // endif mxsz + + MaxSize += mxsz; + } // endfor i + + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Reset read/write position values. */ +/***********************************************************************/ +void TDBTBL::ResetDB(void) + { + for (PCOL colp = Columns; colp; colp = colp->GetNext()) + if (colp->GetAmType() == TYPE_AM_TABID) + colp->COLBLK::Reset(); + + for (PTABLE tblp = Tablist; tblp; tblp = tblp->GetNext()) + ((PTDBASE)tblp->GetTo_Tdb())->ResetDB(); + + Tdbp = (PTDBASE)Tablist->GetTo_Tdb(); + Crp = 0; + } // end of ResetDB + +/***********************************************************************/ +/* Returns RowId if b is false or Rownum if b is true. */ +/***********************************************************************/ +int TDBTBL::RowNumber(PGLOBAL g, bool b) + { + return Tdbp->RowNumber(g) + ((b) ? 0 : Rows); + } // end of RowNumber + +/***********************************************************************/ +/* TBL Access Method opening routine. */ +/* Open first file, other will be opened sequencially when reading. */ +/***********************************************************************/ +bool TDBTBL::OpenDB(PGLOBAL g) + { + if (trace) + htrc("TBL OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n", + this, Tdb_No, Use, To_Key_Col, Mode); + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, replace it at its beginning. */ + /*******************************************************************/ + ResetDB(); + return Tdbp->OpenDB(g); // Re-open fist table + } // endif use + +#if 0 + /*********************************************************************/ + /* Direct access needed for join or sorting. */ + /*********************************************************************/ + if (NeedIndexing(g)) { + // Direct access of TBL tables is not implemented yet + strcpy(g->Message, MSG(NO_MUL_DIR_ACC)); + return TRUE; + } // endif NeedIndexing +#endif // 0 + + /*********************************************************************/ + /* When GetMaxsize was called, To_Filter was not set yet. */ + /*********************************************************************/ + if (To_Filter && Tablist) { + Tablist = NULL; + Nbf = 0; + } // endif To_Filter + + /*********************************************************************/ + /* Open the first table of the list. */ + /*********************************************************************/ + if (!Tablist && InitTableList(g)) // done in GetMaxSize + return TRUE; + + if ((CurTable = Tablist)) { + Tdbp = (PTDBASE)CurTable->GetTo_Tdb(); + Tdbp->SetMode(Mode); +// Tdbp->ResetDB(); +// Tdbp->ResetSize(); + + // Check and initialize the subtable columns + for (PCOL cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetAmType() == TYPE_AM_TABID) + cp->COLBLK::Reset(); + else if (((PTBLCOL)cp)->Init(g)) + return TRUE; + + if (trace) + htrc("Opening subtable %s\n", Tdbp->GetName()); + + // Now we can safely open the table + if (Tdbp->OpenDB(g)) + return TRUE; + + } // endif *Tablist + + Use = USE_OPEN; + return FALSE; + } // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for MUL access method. */ +/***********************************************************************/ +int TDBTBL::ReadDB(PGLOBAL g) + { + int rc; + + if (!CurTable) + return RC_EF; + else if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + strcpy(g->Message, MSG(NO_INDEX_READ)); + rc = RC_FX; + } else { + /*******************************************************************/ + /* Now start the reading process. */ + /*******************************************************************/ + retry: + rc = Tdbp->ReadDB(g); + + if (rc == RC_EF) { + // Total number of rows met so far + Rows += Tdbp->RowNumber(g) - 1; + Crp += Tdbp->GetProgMax(g); + + if ((CurTable = CurTable->GetNext())) { + /***************************************************************/ + /* Continue reading from next table file. */ + /***************************************************************/ + Tdbp->CloseDB(g); + Tdbp = (PTDBASE)CurTable->GetTo_Tdb(); + + // Check and initialize the subtable columns + for (PCOL cp = Columns; cp; cp = cp->GetNext()) + if (cp->GetAmType() == TYPE_AM_TABID) + cp->COLBLK::Reset(); + else if (((PTBLCOL)cp)->Init(g)) + return RC_FX; + + if (trace) + htrc("Opening subtable %s\n", Tdbp->GetName()); + + // Now we can safely open the table + if (Tdbp->OpenDB(g)) // Open next table + return RC_FX; + + goto retry; + } // endif iFile + + } else if (rc == RC_FX) + strcat(strcat(strcat(g->Message, " ("), Tdbp->GetName()), ")"); + + } // endif To_Kindex + + return rc; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base write routine for MUL access method. */ +/***********************************************************************/ +int TDBTBL::WriteDB(PGLOBAL g) + { + strcpy(g->Message, MSG(TABMUL_READONLY)); + return RC_FX; // NIY + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for MUL access method. */ +/***********************************************************************/ +int TDBTBL::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, MSG(TABMUL_READONLY)); + return RC_FX; // NIY + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for MUL access method. */ +/***********************************************************************/ +void TDBTBL::CloseDB(PGLOBAL g) + { + if (Tdbp) + Tdbp->CloseDB(g); + + } // end of CloseDB + +/* ---------------------------- TBLCOL ------------------------------- */ + +/***********************************************************************/ +/* TBLCOL public constructor. */ +/***********************************************************************/ +TBLCOL::TBLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional Dos access method information for column. + Long = cdp->GetLong(); // ??? +//strcpy(F_Date, cdp->F_Date); + Colp = NULL; + To_Val = NULL; + Pseudo = FALSE; + Colnum = cdp->GetOffset(); // If columns are retrieved by number + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of TBLCOL constructor + +#if 0 +/***********************************************************************/ +/* TBLCOL public constructor. */ +/***********************************************************************/ +TBLCOL::TBLCOL(SPCBLK *scp, PTDB tdbp) : COLBLK(scp->GetName(), tdbp, 0) + { + // Set additional TBL access method information for pseudo column. + Is_Key = Was_Key = scp->IsKey(); + Long = scp->GetLength(); + Buf_Type = scp->GetResultType(); + *Format.Type = (Buf_Type == TYPE_INT) ? 'N' : 'C'; + Format.Length = Long; + Colp = NULL; + To_Val = NULL; + Pseudo = TRUE; + } // end of TBLCOL constructor + +/***********************************************************************/ +/* TBLCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +TBLCOL::TBLCOL(TBLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Long = col1->Long; + Colp = col1->Colp; + To_Val = col1->To_Val; + Pseudo = col1->Pseudo; + } // end of TBLCOL copy constructor +#endif + +/***********************************************************************/ +/* TBLCOL initialization routine. */ +/* Look for the matching column in the current table. */ +/***********************************************************************/ +bool TBLCOL::Init(PGLOBAL g) + { + PTDBTBL tdbp = (PTDBTBL)To_Tdb; + + To_Val = NULL; + + if (!(Colp = tdbp->Tdbp->ColDB(g, Name, 0)) && Colnum) + Colp = tdbp->Tdbp->ColDB(g, NULL, Colnum); + + if (Colp) { + Colp->InitValue(g); // May not have been done elsewhere + To_Val = Colp->GetValue(); + } else if (!tdbp->Accept) { + sprintf(g->Message, MSG(NO_MATCHING_COL), Name, tdbp->Tdbp->GetName()); + return TRUE; + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); + } // endif's + + return FALSE; + } // end of Init + +/***********************************************************************/ +/* ReadColumn: */ +/***********************************************************************/ +void TBLCOL::ReadColumn(PGLOBAL g) + { + if (trace) + htrc("TBL ReadColumn: name=%s\n", Name); + + if (Colp) { + Colp->ReadColumn(g); + Value->SetValue_pval(To_Val); + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsNull()); + + } // endif Colp + + } // end of ReadColumn + +/* ---------------------------- TBTBLK ------------------------------- */ + +/***********************************************************************/ +/* ReadColumn: */ +/***********************************************************************/ +void TBTBLK::ReadColumn(PGLOBAL g) + { + if (trace) + htrc("TBT ReadColumn: name=%s\n", Name); + + Value->SetValue_psz((char*)((PTDBTBL)To_Tdb)->Tdbp->GetName()); + + } // end of ReadColumn + +/* ------------------------------------------------------------------- */ diff --git a/storage/connect/tabtbl.h b/storage/connect/tabtbl.h new file mode 100644 index 00000000000..07a20bb2867 --- /dev/null +++ b/storage/connect/tabtbl.h @@ -0,0 +1,162 @@ +/*************** TabTbl H Declares Source Code File (.H) ***************/ +/* Name: TABTBL.H Version 1.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2008-2012 */ +/* */ +/* This file contains the TDBTBL classes declares. */ +/***********************************************************************/ +//#include "osutil.h" +#include "block.h" +#include "colblk.h" + +typedef class TBLDEF *PTBLDEF; +typedef class TDBTBL *PTDBTBL; +typedef class TBLCOL *PTBLCOL; + +/***********************************************************************/ +/* Defines the structure used for multiple tables. */ +/***********************************************************************/ +typedef struct _tablist *PTBL; + +typedef struct _tablist { + PTBL Next; + char *Name; + char *DB; + } TBLIST; + +/***********************************************************************/ +/* TBL table. */ +/***********************************************************************/ +class DllExport TBLDEF : public TABDEF { /* Logical table description */ + friend class TDBTBL; + public: + // Constructor + TBLDEF(void); + + // Implementation + virtual const char *GetType(void) {return "TBL";} + PTBL GetTables(void) {return To_Tables;} +//int GetNtables(void) {return Ntables;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + + protected: + // Members + PTBL To_Tables; /* To the list of tables */ + bool Accept; /* TRUE if bad tables are accepted */ + int Maxerr; /* Maximum number of bad tables */ + int Ntables; /* Number of tables */ + }; // end of TBLDEF + +/***********************************************************************/ +/* This is the TBL Access Method class declaration. */ +/***********************************************************************/ +class DllExport TDBTBL : public TDBASE { + friend class TBLCOL; + friend class TBTBLK; + friend class TDBPLG; + public: + // Constructor + TDBTBL(PTBLDEF tdp = NULL); +//TDBTBL(PTDBTBL tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_TBL;} +//virtual PTDB Duplicate(PGLOBAL g) +// {return (PTDB)new(g) TDBTBL(this);} + + // Methods + virtual void ResetDB(void); +//virtual PTABLE GetTablist(void) {return (PSZ)Tablist;} +//virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void) {return Rows;} + virtual int GetBadLines(void) {return (int)Nbf;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual int GetProgMax(PGLOBAL g); + virtual int GetProgCur(void); + virtual int RowNumber(PGLOBAL g, bool b = FALSE); + virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL scp); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Internal functions + PTDB GetSubTable(PGLOBAL g, PTBL tblp, PTABLE tabp); + bool InitTableList(PGLOBAL g); + bool TestFil(PGLOBAL g, PFIL filp, PTBL tblp); + + // Members + PTABLE Tablist; // Points to the table list + PTABLE CurTable; // Points to the current table + PTDBASE Tdbp; // Current table PTDB + bool Accept; // TRUE if bad tables are accepted + int Maxerr; // Maximum number of bad tables + int Nbf; // Number of bad connections + int Rows; // Used for RowID + int Crp; // Used for CurPos + }; // end of class TDBTBL + +/***********************************************************************/ +/* Class TBLCOL: TBL access method column descriptor. */ +/* This A.M. is used for TBL tables. */ +/***********************************************************************/ +class DllExport TBLCOL : public COLBLK { + friend class TDBTBL; + public: + // Constructors + TBLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "TBL"); + TBLCOL(TBLCOL *colp, PTDB tdbp); // Constructor used in copy process +//TBLCOL(SPCBLK *colp, PTDB tdbp); // Constructor used for pseudo columns + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_TBL;} + + // Methods + virtual bool IsSpecial(void) {return Pseudo;} + virtual void ReadColumn(PGLOBAL g); +//virtual void WriteColumn(PGLOBAL g); +// void Print(PGLOBAL g, FILE *, UINT); + bool Init(PGLOBAL g); + + protected: + // Default constructor not to be used + TBLCOL(void) {} + + // Members + PCOL Colp; // Points to matching table column + PVAL To_Val; // To the matching column value + bool Pseudo; // TRUE for special columns + int Colnum; // Used when retrieving columns by number + }; // end of class TBLCOL + +/***********************************************************************/ +/* Class TBTBLK: TDBPLG TABID special column descriptor. */ +/***********************************************************************/ +class TBTBLK : public TIDBLK { + public: + // The constructor must restore Value because XOBJECT has a void + // constructor called by default that set Value to NULL + TBTBLK(PVAL valp) {Value = valp;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + // Fake operator new used to change TIDBLK into SDTBLK + void * operator new(size_t size, TIDBLK *sp) {return sp;} + +#if !defined(__BORLANDC__) + // Avoid warning C4291 by defining a matching dummy delete operator + void operator delete(void *, TIDBLK*) {} +#endif + + protected: + // Must not have additional members + }; // end of class TBTBLK diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp new file mode 100644 index 00000000000..7a16d2baf0c --- /dev/null +++ b/storage/connect/tabvct.cpp @@ -0,0 +1,565 @@ +/************* TabVct C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABVCT */ +/* ------------- */ +/* Version 3.7 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2012 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This is the TDBVCT and VCTCOL classes implementation routines. */ +/* */ +/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */ +/* -------------------------------------- */ +/* */ +/* REQUIRED FILES: */ +/* --------------- */ +/* TABVCT.C - Source code */ +/* PLGDBSEM.H - DB application declaration file */ +/* TABDOS.H - TABDOS classes declaration file */ +/* GLOBAL.H - Global declaration file */ +/* */ +/* REQUIRED LIBRARIES: */ +/* ------------------- */ +/* Large model C library */ +/* */ +/* REQUIRED PROGRAMS: */ +/* ------------------ */ +/* IBM, Borland, GNU or Microsoft C++ Compiler and Linker */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#if defined(__BORLANDC__) +#define __MFC_COMPAT__ // To define min/max as macro +#endif +//#include <windows.h> +#include <sys/stat.h> +#else +#if defined(UNIX) +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +//#define strerror(X) _strerror(X) +#define NO_ERROR 0 +#else +#include <io.h> +#endif +#include <fcntl.h> +#endif + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "reldef.h" +#include "osutil.h" +#include "filamvct.h" +#include "tabdos.h" +#include "tabvct.h" +#include "valblk.h" + +#if defined(UNIX) +//add dummy strerror (NGC) +char *strerror(int num); +#endif // UNIX + +/***********************************************************************/ +/* Char VCT column blocks are right filled with blanks (blank = true) */ +/* Conversion of block values allowed conditionally for insert only. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, + bool check = true, bool blank = true); + + +/* --------------------------- Class VCTDEF -------------------------- */ + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool VCTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + DOSDEF::DefineAM(g, "BIN", poff); + + Estimate = Cat->GetIntCatInfo("Estimate", 0); + Split = Cat->GetIntCatInfo("Split", (Estimate) ? 0 : 1); + Header = Cat->GetIntCatInfo("Header", 0); + + // CONNECT must have Block/Last info for VEC tables + if (Estimate && !Split && !Header) { + char *fn = Cat->GetStringCatInfo(g, "Filename", "?"); + + // No separate header file fo urbi tables + Header = (*fn == '?') ? 3 : 2; + } // endif Estimate + + Recfm = RECFM_VCT; + + // For packed files the logical record length is calculated in poff + if (poff != Lrecl) { + Lrecl = poff; + Cat->SetIntCatInfo("Lrecl", poff); + } // endif poff + + Padded = false; + Blksize = 0; + return false; + } // end of DefineAM + +/***********************************************************************/ +/* Erase: This was made a separate routine because a strange thing */ +/* happened when DeleteTablefile was defined for the VCTDEF class: */ +/* when called from Catalog, the DOSDEF routine was still called even */ +/* for a VCTDEF class. It also minimizes the specific code. */ +/***********************************************************************/ +bool VCTDEF::Erase(char *filename) + { + bool rc = false; + + if (Split) { + char fpat[_MAX_PATH]; + int i; + PCOLDEF cdp; + + MakeFnPattern(fpat); + + for (i = 1, cdp = To_Cols; cdp; i++, cdp = cdp->GetNext()) { + sprintf(filename, fpat, i); +//#if defined(WIN32) +// rc |= !DeleteFile(filename); +//#else // UNIX + rc |= remove(filename); +//#endif // UNIX + } // endfor cdp + + } else { + rc = DOSDEF::Erase(filename); + + if (Estimate && Header == 2) { + PlugSetPath(filename, Fn, GetPath()); + strcat(PlugRemoveType(filename, filename), ".blk"); + rc |= remove(filename); + } // endif Header + + } // endif Split + + return rc; // Return true if error + } // end of Erase + +/***********************************************************************/ +/* Prepare the column file name pattern for a split table. */ +/* This function returns the number of columns of the table. */ +/***********************************************************************/ +int VCTDEF::MakeFnPattern(char *fpat) + { + char pat[8]; +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ftype[_MAX_EXT]; // File extention + int n, m, ncol = 0; + PCOLDEF cdp; + + for (cdp = To_Cols; cdp; cdp = cdp->GetNext()) + ncol++; + + for (n = 1, m = ncol; m /= 10; n++) ; + + sprintf(pat, "%%0%dd", n); + _splitpath(Fn, drive, direc, fname, ftype); + strcat(fname, pat); + _makepath(fpat, drive, direc, fname, ftype); + PlugSetPath(fpat, fpat, GetPath()); + return ncol; + } // end of MakeFnPattern + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB VCTDEF::GetTable(PGLOBAL g, MODE mode) + { + /*********************************************************************/ + /* Allocate a TDB of the proper type. */ + /* Column blocks will be allocated only when needed. */ + /*********************************************************************/ + // Mapping not used for insert (except for true VEC not split tables) + // or when UseTemp is forced + bool map = Mapped && (Estimate || mode != MODE_INSERT) && + !(PlgGetUser(g)->UseTemp == TMP_FORCE && + (mode == MODE_UPDATE || mode == MODE_DELETE)); + PTXF txfp; + PTDB tdbp; + + if (Multiple) { + strcpy(g->Message, MSG(NO_MUL_VCT)); + return NULL; + } // endif Multiple + + if (Split) { + if (map) + txfp = new(g) VMPFAM(this); + else + txfp = new(g) VECFAM(this); + + } else if (Huge) + txfp = new(g) BGVFAM(this); + else if (map) + txfp = new(g) VCMFAM(this); + else + txfp = new(g) VCTFAM(this); + + tdbp = new(g) TDBVCT(this, txfp); + + /*********************************************************************/ + /* For block tables, get eventually saved optimization values. */ + /*********************************************************************/ + if (mode != MODE_INSERT) + if (tdbp->GetBlockValues(g)) + return NULL; + + return tdbp; + } // end of GetTable + +/* --------------------------- Class TDBVCT -------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBVCT class. */ +/***********************************************************************/ +TDBVCT::TDBVCT(PVCTDEF tdp, PTXF txfp) : TDBFIX(tdp, txfp) + { + To_SetCols = NULL; + } // end of TDBVCT standard constructor + +TDBVCT::TDBVCT(PGLOBAL g, PTDBVCT tdbp) : TDBFIX(g, tdbp) + { + To_SetCols = tdbp->To_SetCols; + } // end of TDBVCT copy constructor + +// Method +PTDB TDBVCT::CopyOne(PTABS t) + { + PTDB tp; + PVCTCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBVCT(g, this); + + for (cp1 = (PVCTCOL)Columns; cp1; cp1 = (PVCTCOL)cp1->Next) { + cp2 = new(g) VCTCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate VCT column description block. */ +/***********************************************************************/ +PCOL TDBVCT::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + return new(g) VCTCOL(g, cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* VCT Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBVCT::OpenDB(PGLOBAL g) + { +#ifdef DEBTRACE + htrc("VCT OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n", + this, Tdb_No, Use, To_Key_Col, Mode); +#endif + + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open, just replace it at its beginning. */ + /*******************************************************************/ + if (To_Kindex) + // Table is to be accessed through a sorted index table + To_Kindex->Reset(); + + Txfp->Rewind(); + return false; + } // endif Use + + /*********************************************************************/ + /* Delete all is not handled using file mapping. */ + /*********************************************************************/ + if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() == TYPE_AM_MAP) { + Txfp = new(g) VCTFAM((PVCTDEF)To_Def); + Txfp->SetTdbp(this); + } // endif Mode + + /*********************************************************************/ + /* Open according to input/output mode required and */ + /* allocate the block buffers for columns used in the query. */ + /*********************************************************************/ + if (Txfp->OpenTableFile(g)) + return true; + + // This was not done in previous version + Use = USE_OPEN; // Do it now in case we are recursively called + + /*********************************************************************/ + /* Reset buffer access according to indexing and to mode. */ + /*********************************************************************/ + Txfp->ResetBuffer(g); + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for VCT access method. */ +/* This routine just set the new block index and record position. */ +/* For index accessed tables the physical reading is deferred to the */ +/* ReadColumn routine so only really used column are physically read. */ +/***********************************************************************/ +int TDBVCT::ReadDB(PGLOBAL g) + { +#ifdef DEBTRACE + fprintf(debug, + "VCT ReadDB: R%d Mode=%d CurBlk=%d CurNum=%d key=%p link=%p Kindex=%p\n", + GetTdb_No(), Mode, Txfp->CurBlk, Txfp->CurNum, + To_Key_Col, To_Link, To_Kindex); +#endif + + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one +// num_there++; + return RC_OK; + default: + /***************************************************************/ + /* Set the file position according to record to read. */ + /***************************************************************/ + if (SetRecpos(g, recpos)) + return RC_FX; + + } // endswitch recpos + + } // endif To_Kindex + + return ReadBuffer(g); + } // end of ReadDB + +/***********************************************************************/ +/* Data Base close routine for VEC access method. */ +/***********************************************************************/ +void TDBVCT::CloseDB(PGLOBAL g) + { + if (To_Kindex) { + To_Kindex->Close(); + To_Kindex = NULL; + } // endif + + Txfp->CloseTableFile(g); + } // end of CloseDB + +// ------------------------ VCTCOL functions ---------------------------- + +/***********************************************************************/ +/* VCTCOL public constructor. */ +/***********************************************************************/ +VCTCOL::VCTCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i) + : DOSCOL(g, cdp, tdbp, cprec, i, "VCT") + { + Deplac = cdp->GetPoff(); + Clen = cdp->GetClen(); // Length of the field in the file + ColBlk = -1; + ColPos = -1; + Blk = NULL; + Modif = 0; + } // end of VCTCOL constructor + +/***********************************************************************/ +/* VCTCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +VCTCOL::VCTCOL(VCTCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp) + { + ColBlk = col1->ColBlk; + ColPos = col1->ColPos; + Blk = col1->Blk; // Should be NULL when copying ???? + Modif = col1->Modif; // Should be 0 ???? + } // end of VCTCOL copy constructor + +/***********************************************************************/ +/* SetBuffer: allocate and set the buffers needed for write operation.*/ +/***********************************************************************/ +bool VCTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + // Eventual conversion will be done when setting ValBlk from Value. + Value = value; // Force To_Val == Value + + if (DOSCOL::SetBuffer(g, value, ok, check)) + return true; + + if (To_Tdb->GetMode() != MODE_INSERT) { + // Allocate the block buffer to use for read/writing except when + // updating a mapped VCT table and Ok is true. + PTDBVCT tdbp = (PTDBVCT)To_Tdb; + + if (tdbp->Txfp->GetAmType() == TYPE_AM_VMP && ok) { + Blk = AllocValBlock(g, (void*)1, Buf_Type, tdbp->Txfp->Nrec, + Format.Length, + Format.Prec, check); + Status |= BUF_MAPPED; // Will point into mapped file + } else + Blk = AllocValBlock(g, NULL, Buf_Type, tdbp->Txfp->Nrec, + Format.Length, + Format.Prec, check); + } // endif Mode + + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* ReadBlock: Indicate it is Ok to make updates. */ +/***********************************************************************/ +void VCTCOL::SetOk(void) + { + if (((PTDBVCT)To_Tdb)->Txfp->GetAmType() == TYPE_AM_VMP) + Status |= BUF_MAPPED; + + Status |= BUF_EMPTY; + Modif = 0; + } // end of SetOk + +/***********************************************************************/ +/* ReadBlock: Read column values from current block. */ +/***********************************************************************/ +void VCTCOL::ReadBlock(PGLOBAL g) + { + PVCTFAM txfp = (PVCTFAM)((PTDBVCT)To_Tdb)->Txfp; + +#if defined(_DEBUG) + if (!Blk) { + strcpy(g->Message, MSG(TO_BLK_IS_NULL)); + longjmp(g->jumper[g->jump_level], 58); + } // endif +#endif + + /*********************************************************************/ + /* Read column block according to used access method. */ + /*********************************************************************/ + if (txfp->ReadBlock(g, this)) + longjmp(g->jumper[g->jump_level], 6); + + ColBlk = txfp->CurBlk; + ColPos = -1; // Any invalid position + } // end of ReadBlock + +/***********************************************************************/ +/* WriteBlock: Write back current column values for one block. */ +/* Note: the test of Status is meant to prevent physical writing of */ +/* the block during the checking loop in mode Update. It is set to */ +/* BUF_EMPTY when reopening the table between the two loops. */ +/***********************************************************************/ +void VCTCOL::WriteBlock(PGLOBAL g) + { + if (Modif && (Status & BUF_EMPTY)) { + PVCTFAM txfp = (PVCTFAM)((PTDBVCT)To_Tdb)->Txfp; + +#if defined(_DEBUG) + if (!Blk) { + strcpy(g->Message, MSG(BLK_IS_NULL)); + longjmp(g->jumper[g->jump_level], 56); + } // endif +#endif + + /*******************************************************************/ + /* Write column block according to used access method. */ + /*******************************************************************/ + if (txfp->WriteBlock(g, this)) + longjmp(g->jumper[g->jump_level], 6); + + Modif = 0; + } // endif Modif + + } // end of WriteBlock + +/***********************************************************************/ +/* ReadColumn: what this routine does is to check whether a column */ +/* block has been read from the file, then to extract from it the */ +/* field corresponding to this column and convert it to buffer type. */ +/***********************************************************************/ +void VCTCOL::ReadColumn(PGLOBAL g) + { + PTXF txfp = ((PTDBVCT)To_Tdb)->Txfp; + +#if defined(_DEBUG) || defined(DEBTRACE) + assert (!To_Kcol); +#endif + +#ifdef DEBTRACE + fprintf(debug, + "VCT ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n", + Name, To_Tdb->GetTdb_No(), ColUse, Status, Buf_Type); +#endif + + if (ColBlk != txfp->CurBlk) + ReadBlock(g); + else if (ColPos == txfp->CurNum) + return; // Value is already there + +//ColBlk = txfp->CurBlk; done in ReadBlock + ColPos = txfp->CurNum; + Value->SetValue_pvblk(Blk, ColPos); + + // Set null when applicable + if (Nullable) + Value->SetNull(Value->IsZero()); + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: Modifications are written back into column buffer. */ +/* On each change of block the buffer is written back to file and */ +/* in mode Insert the buffer is filled with the block to update. */ +/***********************************************************************/ +void VCTCOL::WriteColumn(PGLOBAL g) + { + PTXF txfp = ((PTDBVCT)To_Tdb)->Txfp;; + +#ifdef DEBTRACE + fprintf(debug, + "VCT WriteColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n", + Name, To_Tdb->GetTdb_No(), ColUse, Status, Buf_Type); +#endif + + ColBlk = txfp->CurBlk; + ColPos = txfp->CurNum; + Blk->SetValue(Value, ColPos); + Modif++; + } // end of WriteColumn + +/* ------------------------ End of TabVct ---------------------------- */ diff --git a/storage/connect/tabvct.h b/storage/connect/tabvct.h new file mode 100644 index 00000000000..4049b4f7683 --- /dev/null +++ b/storage/connect/tabvct.h @@ -0,0 +1,123 @@ +/*************** TabVct H Declares Source Code File (.H) ***************/ +/* Name: TABVCT.H Version 3.4 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2011 */ +/* */ +/* This file contains the TDBVCT class declares. */ +/***********************************************************************/ +#ifndef __TABVCT__ +#define __TABVCT__ + +#include "tabfix.h" +#if defined(UNIX) +//#include <string.h.SUNWCCh> +#endif + +typedef class TDBVCT *PTDBVCT; +typedef class VCTCOL *PVCTCOL; + +/***********************************************************************/ +/* VCT table. */ +/***********************************************************************/ +class DllExport VCTDEF : public DOSDEF { /* Logical table description */ + friend class VCTFAM; + friend class VECFAM; + friend class VMPFAM; + public: + // Constructor + VCTDEF(void) {Split = Estimate = Header = 0;} + + // Implementation + virtual const char *GetType(void) {return "VCT";} + int GetEstimate(void) {return Estimate;} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE mode); + + protected: + // Specific file erase routine for vertical tables + virtual bool Erase(char *filename); + int MakeFnPattern(char *fpat); + + // Members + int Split; /* Columns in separate files */ + int Estimate; /* Estimated maximum size of table */ + int Header; /* 0: no, 1: separate, 2: in data file */ + }; // end of VCTDEF + +/***********************************************************************/ +/* This is the DOS/UNIX Access Method class declaration for files */ +/* in blocked vector format. In each block containing "Elements" */ +/* records, values of each columns are consecutively stored (vector). */ +/***********************************************************************/ +class DllExport TDBVCT : public TDBFIX { + friend class VCTCOL; + friend class VCTFAM; + friend class VCMFAM; + friend class VECFAM; + friend class VMPFAM; + public: + // Constructors + TDBVCT(PVCTDEF tdp, PTXF txfp); + TDBVCT(PGLOBAL g, PTDBVCT tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_VCT;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBVCT(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual void CloseDB(PGLOBAL g); + + protected: + // Members + }; // end of class TDBVCT + +/***********************************************************************/ +/* Class VCTCOL: VCT access method column descriptor. */ +/* This A.M. is used for file having column wise organization. */ +/***********************************************************************/ +class DllExport VCTCOL : public DOSCOL { + friend class TDBVCT; + friend class VCTFAM; + friend class VCMFAM; + friend class VECFAM; + friend class VMPFAM; + friend class BGVFAM; + public: + // Constructors + VCTCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i); + VCTCOL(VCTCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_VCT;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void SetOk(void); + + protected: + virtual void ReadBlock(PGLOBAL g); + virtual void WriteBlock(PGLOBAL g); + + VCTCOL(void) {} // Default constructor not to be used + + // Members + PVBLK Blk; // Block buffer + int Clen; // Internal length in table + int ColBlk; // Block pointed by column + int ColPos; // Last position read + int Modif; // Number of modified lines in block + }; // end of class VCTCOL + +#endif // __TABVCT__ + diff --git a/storage/connect/tabwmi.cpp b/storage/connect/tabwmi.cpp new file mode 100644 index 00000000000..d89d61dc11c --- /dev/null +++ b/storage/connect/tabwmi.cpp @@ -0,0 +1,851 @@ +/***********************************************************************/ +/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013 */ +/* TABWMI: Virtual table to get WMI information. */ +/***********************************************************************/ +#if !defined(WIN32) +#error This is a WIN32 only table type +#endif // !WIN32 +#include "my_global.h" +#include <stdio.h> + +#include "global.h" +#include "plgdbsem.h" +#include "mycat.h" +#include "reldef.h" +#include "xtable.h" +#include "colblk.h" +#include "filter.h" +//#include "xindex.h" +#include "tabwmi.h" +#include "valblk.h" +#include "plgcnx.h" // For DB types +#include "resource.h" + +extern "C" int trace; + +/* ------------------- Functions WMI Column info --------------------- */ + +/***********************************************************************/ +/* Initialize WMI operations. */ +/***********************************************************************/ +PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname) +{ + IWbemLocator *loc; + char *p; + HRESULT res; + PWMIUT wp = (PWMIUT)PlugSubAlloc(g, NULL, sizeof(WMIUTIL)); + + if (trace) + htrc("WMIColumns class %s space %s\n", SVP(classname), SVP(nsp)); + + /*********************************************************************/ + /* Set default values for the namespace and class name. */ + /*********************************************************************/ + if (!nsp) + nsp = "root\\cimv2"; + + if (!classname) { + if (!stricmp(nsp, "root\\cimv2")) + classname = "ComputerSystemProduct"; + else if (!stricmp(nsp, "root\\cli")) + classname = "Msft_CliAlias"; + else { + strcpy(g->Message, "Missing class name"); + return NULL; + } // endif classname + + } // endif classname + + /*********************************************************************/ + /* Initialize WMI. */ + /*********************************************************************/ +//res = CoInitializeEx(NULL, COINIT_MULTITHREADED); + res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + if (FAILED(res)) { + sprintf(g->Message, "Failed to initialize COM library. " + "Error code = %p", res); + return NULL; + } // endif res + +#if 0 // irrelevant for a DLL + res = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_CONNECT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_NONE, NULL); + + if (res != RPC_E_TOO_LATE && FAILED(res)) { + sprintf(g->Message, "Failed to initialize security. " + "Error code = %p", res); + CoUninitialize(); + return NULL; + } // endif Res +#endif // 0 + + res = CoCreateInstance(CLSID_WbemLocator, NULL, + CLSCTX_INPROC_SERVER, IID_IWbemLocator, + (void**) &loc); + if (FAILED(res)) { + sprintf(g->Message, "Failed to create Locator. " + "Error code = %p", res); + CoUninitialize(); + return NULL; + } // endif res + + res = loc->ConnectServer(_bstr_t(nsp), + NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc); + + if (FAILED(res)) { + sprintf(g->Message, "Could not connect. Error code = %p", res); + loc->Release(); + CoUninitialize(); + return NULL; + } // endif res + + loc->Release(); + + if (trace) + htrc("Successfully connected to namespace.\n"); + + /*********************************************************************/ + /* Perform a full class object retrieval. */ + /*********************************************************************/ + p = (char*)PlugSubAlloc(g, NULL, strlen(classname) + 7); + + if (strchr(classname, '_')) + strcpy(p, classname); + else + strcat(strcpy(p, "Win32_"), classname); + + res = wp->Svc->GetObject(bstr_t(p), 0, 0, &wp->Cobj, 0); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetObject %s in %s\n", classname, nsp); + wp->Svc->Release(); + wp->Svc = NULL; // MUST be set to NULL (why?) + return NULL; + } // endif res + + return wp; +} // end of InitWMI + +/***********************************************************************/ +/* WMIColumns: constructs the result blocks containing the description */ +/* of all the columns of a WMI table of a specified class. */ +/***********************************************************************/ +PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info) + { + static int dbtype[] = {DB_CHAR, DB_SHORT, DB_CHAR, + DB_INT, DB_INT, DB_SHORT}; + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, + TYPE_INT, TYPE_INT, TYPE_SHORT}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, + FLD_PREC, FLD_LENGTH, FLD_SCALE}; + static unsigned int len, length[] = {0, 6, 8, 10, 10, 6}; + int i = 0, n = 0, ncol = sizeof(dbtype) / sizeof(int); + int lng, typ, prec; + LONG low, upp; + BSTR propname; + VARIANT val; + CIMTYPE type; + HRESULT res; + PWMIUT wp; + SAFEARRAY *prnlist = NULL; + PQRYRES qrp = NULL; + PCOLRES crp; + + if (!info) { + /*******************************************************************/ + /* Initialize WMI if not done yet. */ + /*******************************************************************/ + if (!(wp = InitWMI(g, nsp, cls))) + return NULL; + + /*******************************************************************/ + /* Get the number of properties to return. */ + /*******************************************************************/ + res = wp->Cobj->Get(bstr_t("__Property_Count"), 0, &val, NULL, NULL); + + if (FAILED(res)) { + sprintf(g->Message, "failed Get(__Property_Count) res=%d\n", res); + goto err; + } // endif res + + if (!(n = val.lVal)) { + sprintf(g->Message, "Class %s in %s has no properties\n", + cls, nsp); + goto err; + } // endif res + + /*******************************************************************/ + /* Get max property name length. */ + /*******************************************************************/ + res = wp->Cobj->GetNames(NULL, + WBEM_FLAG_ALWAYS | WBEM_FLAG_NONSYSTEM_ONLY, + NULL, &prnlist); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetNames res=%d\n", res); + goto err; + } // endif res + + res = SafeArrayGetLBound(prnlist, 1, &low); + res = SafeArrayGetUBound(prnlist, 1, &upp); + + for (long i = low; i <= upp; i++) { + // Get this property name. + res = SafeArrayGetElement(prnlist, &i, &propname); + + if (FAILED(res)) { + sprintf(g->Message, "failed GetArrayElement res=%d\n", res); + goto err; + } // endif res + + len = (unsigned)SysStringLen(propname); + length[0] = max(length[0], len); + } // enfor i + + res = SafeArrayDestroy(prnlist); + } else + length[0] = 128; + + /*********************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /*********************************************************************/ + qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3, + dbtype, buftyp, fldtyp, length, true, true); + + if (info) + return qrp; + + /*********************************************************************/ + /* Now get the results into blocks. */ + /*********************************************************************/ + res = wp->Cobj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY); + + if (FAILED(res)) { + sprintf(g->Message, "failed BeginEnumeration hr=%d\n", res); + qrp = NULL; + goto err; + } // endif hr + + while (TRUE) { + res = wp->Cobj->Next(0, &propname, &val, &type, NULL); + + if (FAILED(res)) { + sprintf(g->Message, "failed getting Next hr=%d\n", res); + qrp = NULL; + goto err; + } else if (res == WBEM_S_NO_MORE_DATA) { + VariantClear(&val); + break; + } // endif res + + if (i >= n) + break; // Should never happen + else + prec = 0; + + switch (type) { + case CIM_STRING: + typ = TYPE_STRING; + lng = 255; + prec = 1; // Case insensitive + break; + case CIM_SINT32: + case CIM_UINT32: + case CIM_BOOLEAN: + typ = TYPE_INT; + lng = 11; + break; + case CIM_SINT8: + case CIM_UINT8: + typ = TYPE_TINY; + lng = 4; + break; + case CIM_SINT16: + case CIM_UINT16: + typ = TYPE_SHORT; + lng = 6; + break; + case CIM_REAL64: + case CIM_REAL32: + prec = 2; + typ = TYPE_FLOAT; + lng = 15; + break; + case CIM_SINT64: + case CIM_UINT64: + typ = TYPE_BIGINT; + lng = 20; + break; + case CIM_DATETIME: + typ = TYPE_DATE; + lng = 19; + break; + case CIM_CHAR16: + typ = TYPE_STRING; + lng = 16; + break; + case CIM_EMPTY: + typ = TYPE_STRING; + lng = 24; // ??? + break; + default: + qrp->BadLines++; + goto suite; + } // endswitch type + + crp = qrp->Colresp; // Column Name + crp->Kdata->SetValue(_com_util::ConvertBSTRToString(propname), i); + crp = crp->Next; // Data Type + crp->Kdata->SetValue(typ, i); + crp = crp->Next; // Type Name + crp->Kdata->SetValue(GetTypeName(typ), i); + crp = crp->Next; // Precision + crp->Kdata->SetValue(lng, i); + crp = crp->Next; // Length + crp->Kdata->SetValue(lng, i); + crp = crp->Next; // Scale (precision) + crp->Kdata->SetValue(prec, i); + i++; + + suite: + SysFreeString(propname); + VariantClear(&val); + } // endfor i + + qrp->Nblin = i; + + err: + // Cleanup + wp->Cobj->Release(); + wp->Svc->Release(); + wp->Svc = NULL; // MUST be set to NULL (why?) + CoUninitialize(); + + /*********************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /*********************************************************************/ + return qrp; + } // end of WMIColumns + +/* -------------- Implementation of the WMI classes ------------------ */ + +/***********************************************************************/ +/* DefineAM: define specific AM values for WMI table. */ +/***********************************************************************/ +bool WMIDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + Nspace = Cat->GetStringCatInfo(g, "Namespace", "Root\\CimV2"); + Wclass = Cat->GetStringCatInfo(g, "Class", + (!stricmp(Nspace, "root\\cimv2") ? "ComputerSystemProduct" : + !stricmp(Nspace, "root\\cli") ? "Msft_CliAlias" : "")); + + if (!*Wclass) { + sprintf(g->Message, "Missing class name for %s", Nspace); + return true; + } else if (!strchr(Wclass, '_')) { + char *p = (char*)PlugSubAlloc(g, NULL, strlen(Wclass) + 7); + Wclass = strcat(strcpy(p, "Win32_"), Wclass); + } // endif Wclass + + if (Catfunc == FNC_NO) + Ems = Cat->GetIntCatInfo("Estimate", 100); + + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB WMIDEF::GetTable(PGLOBAL g, MODE m) + { + if (Catfunc == FNC_NO) + return new(g) TDBWMI(this); + else if (Catfunc == FNC_COL) + return new(g) TDBWCL(this); + + sprintf(g->Message, "Bad catfunc %ud for WMI", Catfunc); + return NULL; + } // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBWMI class. */ +/***********************************************************************/ +TDBWMI::TDBWMI(PWMIDEF tdp) : TDBASE(tdp) + { + Svc = NULL; + Enumerator = NULL; + ClsObj = NULL; + Nspace = tdp->Nspace; + Wclass = tdp->Wclass; + ObjPath = NULL; + Kvp = NULL; + Ems = tdp->Ems; + Kcol = NULL; + Vbp = NULL; + Init = false; + Done = false; + Res = 0; + Rc = 0; + N = -1; + } // end of TDBWMI constructor + +/***********************************************************************/ +/* Allocate WMI column description block. */ +/***********************************************************************/ +PCOL TDBWMI::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PCOL colp; + + colp = new(g) WMICOL(cdp, this, n); + + if (cprec) { + colp->SetNext(cprec->GetNext()); + cprec->SetNext(colp); + } else { + colp->SetNext(Columns); + Columns = colp; + } // endif cprec + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* Initialize: Initialize WMI operations. */ +/***********************************************************************/ +bool TDBWMI::Initialize(PGLOBAL g) + { + if (Init) + return false; + + // Initialize COM. + Res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + if (FAILED(Res)) { + sprintf(g->Message, "Failed to initialize COM library. " + "Error code = %p", Res); + return true; // Program has failed. + } // endif Res + + // Obtain the initial locator to Windows Management + // on a particular host computer. + IWbemLocator *loc; // Initial Windows Management locator + + Res = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID*) &loc); + + if (FAILED(Res)) { + sprintf(g->Message, "Failed to create Locator. " + "Error code = %p", Res); + CoUninitialize(); + return true; // Program has failed. + } // endif Res + + // Connect to the specified namespace with the + // current user and obtain pointer to Svc + // to make IWbemServices calls. + Res = loc->ConnectServer(_bstr_t(Nspace), + NULL, NULL,0, NULL, 0, 0, &Svc); + + if (FAILED(Res)) { + sprintf(g->Message, "Could not connect. Error code = %p", Res); + loc->Release(); + CoUninitialize(); + return true; // Program has failed. + } // endif hres + + loc->Release(); // Not used anymore + + // Set the IWbemServices proxy so that impersonation + // of the user (client) occurs. + Res = CoSetProxyBlanket(Svc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, + NULL, RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + + if (FAILED(Res)) { + sprintf(g->Message, "Could not set proxy. Error code = 0x", Res); + Svc->Release(); + CoUninitialize(); + return true; // Program has failed. + } // endif Res + + Init = true; + return false; + } // end of Initialize + +/***********************************************************************/ +/* Changes '\' into '\\' in the filter. */ +/***********************************************************************/ +void TDBWMI::DoubleSlash(PGLOBAL g) + { + if (To_Filter && strchr(To_Filter, '\\')) { + char *buf = (char*)PlugSubAlloc(g, NULL, strlen(To_Filter) * 2); + int i = 0, k = 0; + + do { + if (To_Filter[i] == '\\') + buf[k++] = '\\'; + + buf[k++] = To_Filter[i]; + } while (To_Filter[i++]); + + To_Filter = buf; + } // endif To_Filter + + } // end of DoubleSlash + +/***********************************************************************/ +/* MakeWQL: make the WQL statement use with WMI ExecQuery. */ +/***********************************************************************/ +char *TDBWMI::MakeWQL(PGLOBAL g) + { + char *colist, *wql/*, *pw = NULL*/; + int len, ncol = 0; + bool first = true, noloc = false; + PCOL colp; + + // Normal WQL statement to retrieve results + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial() && (colp->GetColUse(U_P | U_J_EXT) || noloc)) + ncol++; + + if (ncol) { + colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol); + + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + if (colp->GetResultType() == TYPE_DATE) + ((DTVAL*)colp->GetValue())->SetFormat(g, "YYYYMMDDhhmmss", 19); + + if (colp->GetColUse(U_P | U_J_EXT) || noloc) { + if (first) { + strcpy(colist, colp->GetName()); + first = false; + } else + strcat(strcat(colist, ", "), colp->GetName()); + + } // endif ColUse + + } // endif Special + + } else { + // ncol == 0 can occur for queries such that sql count(*) from... + // for which we will count the rows from sql * from... + colist = (char*)PlugSubAlloc(g, NULL, 2); + strcpy(colist, "*"); + } // endif ncol + + // Below 14 is length of 'select ' + length of ' from ' + 1 + len = (strlen(colist) + strlen(Wclass) + 14); + len += (To_Filter ? strlen(To_Filter) + 7 : 0); + wql = (char*)PlugSubAlloc(g, NULL, len); + strcat(strcat(strcpy(wql, "SELECT "), colist), " FROM "); + strcat(wql, Wclass); + + if (To_Filter) + strcat(strcat(wql, " WHERE "), To_Filter); + + return wql; + } // end of MakeWQL + +/***********************************************************************/ +/* GetWMIInfo: Get info for the WMI class. */ +/***********************************************************************/ +bool TDBWMI::GetWMIInfo(PGLOBAL g) + { + if (Done) + return false; + + char *cmd = MakeWQL(g); + + if (cmd == NULL) { + sprintf(g->Message, "Error making WQL statement"); + Svc->Release(); + CoUninitialize(); + return true; // Program has failed. + } // endif cmd + + // Query for Wclass in Nspace + Rc = Svc->ExecQuery(bstr_t("WQL"), bstr_t(cmd), +// WBEM_FLAG_BIDIRECTIONAL | WBEM_FLAG_RETURN_IMMEDIATELY, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &Enumerator); + + if (FAILED(Rc)) { + sprintf(g->Message, "Query %s failed. Error code = %p", cmd, Rc); + Svc->Release(); + CoUninitialize(); + return true; // Program has failed. + } // endif Rc + + Done = true; + return false; + } // end of GetWMIInfo + +/***********************************************************************/ +/* WMI: Get the number returned instances. */ +/***********************************************************************/ +int TDBWMI::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + /*******************************************************************/ + /* Loop enumerating to get the count. This is prone to last a */ + /* very long time for some classes such as DataFile, this is why */ + /* we just return an estimated value that will be ajusted later. */ + /*******************************************************************/ + MaxSize = Ems; +#if 0 + if (Initialize(g)) + return -1; + else if (GetWMIInfo(g)) + return -1; + else + MaxSize = 0; + + PDBUSER dup = PlgGetUser(g); + + while (Enumerator) { + Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc); + + if (Rc == 0) + break; + + MaxSize++; + } // endwile Enumerator + + Res = Enumerator->Reset(); +#endif // 0 + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* When making a Kindex, must provide the Key column info. */ +/***********************************************************************/ +int TDBWMI::GetRecpos(void) + { + if (!Kcol || !Vbp) + return N; + + Kcol->Reset(); + Kcol->Eval(NULL); + Vbp->SetValue(Kcol->GetValue(), N); + return N; + } // end of GetRecpos + +/***********************************************************************/ +/* WMI Access Method opening routine. */ +/***********************************************************************/ +bool TDBWMI::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open. */ + /*******************************************************************/ + Res = Enumerator->Reset(); + N = 0; + return false; + } // endif use + + if (Mode != MODE_READ) { + /*******************************************************************/ + /* WMI tables cannot be modified. */ + /*******************************************************************/ + strcpy(g->Message, "WMI tables are read only"); + return true; + } // endif Mode + + if (!To_Filter && !stricmp(Wclass, "CIM_Datafile") + && !stricmp(Nspace, "root\\cimv2")) { + strcpy(g->Message, + "Would last forever when not filtered, use DIR table instead"); + return true; + } else + DoubleSlash(g); + + /*********************************************************************/ + /* Initialize the WMI processing. */ + /*********************************************************************/ + if (Initialize(g)) + return true; + else + return GetWMIInfo(g); + + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for WMI access method. */ +/***********************************************************************/ +int TDBWMI::ReadDB(PGLOBAL g) + { + Res = Enumerator->Next(WBEM_INFINITE, 1, &ClsObj, &Rc); + + if (Rc == 0) + return RC_EF; + + N++; + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for WMI access methods. */ +/***********************************************************************/ +int TDBWMI::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "WMI tables are read only"); + return RC_FX; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for WMI access methods. */ +/***********************************************************************/ +int TDBWMI::DeleteDB(PGLOBAL g, int irc) + { + strcpy(g->Message, "Delete not enabled for WMI tables"); + return RC_FX; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for WMI access method. */ +/***********************************************************************/ +void TDBWMI::CloseDB(PGLOBAL g) + { + // Cleanup + if (ClsObj) + ClsObj->Release(); + + if (Enumerator) + Enumerator->Release(); + + if (Svc) + Svc->Release(); + + CoUninitialize(); + } // end of CloseDB + +// ------------------------ WMICOL functions ---------------------------- + +/***********************************************************************/ +/* WMICOL public constructor. */ +/***********************************************************************/ +WMICOL::WMICOL(PCOLDEF cdp, PTDB tdbp, int n) + : COLBLK(cdp, tdbp, n) + { + Tdbp = (PTDBWMI)tdbp; + VariantInit(&Prop); + Ctype = CIM_ILLEGAL; + Res = 0; + } // end of WMICOL constructor + +#if 0 +/***********************************************************************/ +/* WMICOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +WMICOL::WMICOL(WMICOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + } // end of WMICOL copy constructor +#endif // 0 + +/***********************************************************************/ +/* Read the next WMI address elements. */ +/***********************************************************************/ +void WMICOL::ReadColumn(PGLOBAL g) + { + // Get the value of the Name property + Res = Tdbp->ClsObj->Get(_bstr_t(Name), 0, &Prop, &Ctype, 0); + + switch (Prop.vt) { + case VT_EMPTY: + case VT_NULL: + case VT_VOID: + Value->Reset(); + break; + case VT_BSTR: + Value->SetValue_psz(_com_util::ConvertBSTRToString(Prop.bstrVal)); + break; + case VT_I4: + case VT_UI4: + Value->SetValue(Prop.lVal); + break; + case VT_I2: + case VT_UI2: + Value->SetValue(Prop.iVal); + break; + case VT_INT: + case VT_UINT: + Value->SetValue((int)Prop.intVal); + break; + case VT_BOOL: + Value->SetValue(((int)Prop.boolVal) ? 1 : 0); + break; + case VT_R8: + Value->SetValue(Prop.dblVal); + break; + case VT_R4: + Value->SetValue((double)Prop.fltVal); + break; + case VT_DATE: + switch (Value->GetType()) { + case TYPE_DATE: + {SYSTEMTIME stm; + struct tm ptm; + int rc = VariantTimeToSystemTime(Prop.date, &stm); + + ptm.tm_year = stm.wYear; + ptm.tm_mon = stm.wMonth; + ptm.tm_mday = stm.wDay; + ptm.tm_hour = stm.wHour; + ptm.tm_min = stm.wMinute; + ptm.tm_sec = stm.wSecond; + ((DTVAL*)Value)->MakeTime(&ptm); + }break; + case TYPE_STRING: + {SYSTEMTIME stm; + char buf[24]; + int rc = VariantTimeToSystemTime(Prop.date, &stm); + + sprintf(buf, "%02d/%02d/%d %02d:%02d:%02d", + stm.wDay, stm.wMonth, stm.wYear, + stm.wHour, stm.wMinute, stm.wSecond); + Value->SetValue_psz(buf); + }break; + default: + Value->SetValue((double)Prop.fltVal); + } // endswitch Type + + break; + default: + // This will reset numeric column value + Value->SetValue_psz("Type not supported"); + break; + } // endswitch vt + + VariantClear(&Prop); + } // end of ReadColumn + +/* ---------------------------TDBWCL class --------------------------- */ + +/***********************************************************************/ +/* TDBWCL class constructor. */ +/***********************************************************************/ +TDBWCL::TDBWCL(PWMIDEF tdp) : TDBCAT(tdp) + { + Nsp = tdp->Nspace; + Cls = tdp->Wclass; + } // end of TDBWCL constructor + +/***********************************************************************/ +/* GetResult: Get the list of the WMI class properties. */ +/***********************************************************************/ +PQRYRES TDBWCL::GetResult(PGLOBAL g) + { + return WMIColumns(g, Nsp, Cls, false); + } // end of GetResult + + diff --git a/storage/connect/tabwmi.h b/storage/connect/tabwmi.h new file mode 100644 index 00000000000..9df57e7c579 --- /dev/null +++ b/storage/connect/tabwmi.h @@ -0,0 +1,151 @@ +// TABWMI.H Olivier Bertrand 2012 +// WMI: Virtual table to Get WMI information +#define _WIN32_DCOM +#include <wbemidl.h> +# pragma comment(lib, "wbemuuid.lib") +#include <iostream> +using namespace std; +#include <comdef.h> + +/***********************************************************************/ +/* Definitions. */ +/***********************************************************************/ +typedef class WMIDEF *PWMIDEF; +typedef class TDBWMI *PTDBWMI; +typedef class WMICOL *PWMICOL; +typedef class TDBWCL *PTDBWCL; +typedef class WCLCOL *PWCLCOL; + +/***********************************************************************/ +/* Structure used by WMI column info functions. */ +/***********************************************************************/ +typedef struct _WMIutil { + IWbemServices *Svc; + IWbemClassObject *Cobj; +} WMIUTIL, *PWMIUT; + +/***********************************************************************/ +/* Functions used externally. */ +/***********************************************************************/ +PQRYRES WMIColumns(PGLOBAL g, char *nsp, char *cls, bool info); + +/* -------------------------- WMI classes ---------------------------- */ + +/***********************************************************************/ +/* WMI: Virtual table to get the WMI information. */ +/***********************************************************************/ +class WMIDEF : public TABDEF { /* Logical table description */ + friend class TDBWMI; + friend class TDBWCL; + friend class TDBWCX; + public: + // Constructor + WMIDEF(void) {Pseudo = 3; Nspace = NULL; Wclass = NULL; Ems = 0;} + + // Implementation + virtual const char *GetType(void) {return "WMI";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + virtual bool DeleteTableFile(PGLOBAL g) {return true;} + + protected: + // Members + char *Nspace; + char *Wclass; + int Ems; + }; // end of WMIDEF + +/***********************************************************************/ +/* This is the class declaration for the WMI table. */ +/***********************************************************************/ +class TDBWMI : public TDBASE { + friend class WMICOL; + public: + // Constructor + TDBWMI(PWMIDEF tdp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_WMI;} + + // Methods + virtual int GetRecpos(void); + virtual int GetProgCur(void) {return N;} + virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Specific routines + bool Initialize(PGLOBAL g); + char *MakeWQL(PGLOBAL g); + void DoubleSlash(PGLOBAL g); + bool GetWMIInfo(PGLOBAL g); + + // Members + IWbemServices *Svc; // IWbemServices pointer + IEnumWbemClassObject *Enumerator; + IWbemClassObject *ClsObj; + char *Nspace; // Namespace + char *Wclass; // Class name + char *ObjPath; // Used for direct access + char *Kvp; // Itou + int Ems; // Estimated max size + PCOL Kcol; // Key column + HRESULT Res; + PVBLK Vbp; + bool Init; + bool Done; + ULONG Rc; + int N; // Row number + }; // end of class TDBWMI + +/***********************************************************************/ +/* Class WMICOL: WMI Address column. */ +/***********************************************************************/ +class WMICOL : public COLBLK { + friend class TDBWMI; + public: + // Constructors + WMICOL(PCOLDEF cdp, PTDB tdbp, int n); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_WMI;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + protected: + WMICOL(void) {} // Default constructor not to be used + + // Members + PTDBWMI Tdbp; // Points to WMI table block + VARIANT Prop; // Property value + CIMTYPE Ctype; // CIM Type + HRESULT Res; + }; // end of class WMICOL + +/***********************************************************************/ +/* This is the class declaration for the WMI catalog table. */ +/***********************************************************************/ +class TDBWCL : public TDBCAT { + public: + // Constructor + TDBWCL(PWMIDEF tdp); + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g); + + // Members + char *Nsp; // Name space + char *Cls; // Class + }; // end of class TDBWCL diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp new file mode 100644 index 00000000000..9b8db7abbab --- /dev/null +++ b/storage/connect/tabxml.cpp @@ -0,0 +1,1763 @@ +/************* Tabxml C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABXML */ +/* ------------- */ +/* Version 2.6 */ +/* */ +/* Author Olivier BERTRAND 2007 - 2013 */ +/* */ +/* This program are the XML tables classes using MS-DOM or libxml2. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include required compiler header files. */ +/***********************************************************************/ +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#if defined(WIN32) +#include <io.h> +#include <winsock2.h> +//#include <windows.h> +#include <comdef.h> +#else // !WIN32 +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +//#include <ctype.h> +#include "osutil.h" +#define _O_RDONLY O_RDONLY +#endif // !WIN32 +#include "my_global.h" + +#define INCLUDE_TDBXML +#define NODE_TYPE_LIST + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* tabdos.h is header containing the TABDOS class declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "reldef.h" +#include "xtable.h" +#include "colblk.h" +#include "xindex.h" +#include "plgxml.h" +#include "tabxml.h" + +extern "C" { +extern char version[]; +extern int trace; +} // "C" + +#if defined(WIN32) && defined(DOMDOC_SUPPORT) +#define XMLSUP "MS-DOM" +#else // !WIN32 +#define XMLSUP "libxml2" +#endif // !WIN32 + +/* -------------- Implementation of the XMLDEF class ---------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +XMLDEF::XMLDEF(void) + { + Pseudo = 3; + Fn = NULL; + Encoding = NULL; + Tabname = NULL; + Rowname = NULL; + Colname = NULL; + Mulnode = NULL; + XmlDB = NULL; + Nslist = NULL; + DefNs = NULL; + Attrib = NULL; + Hdattr = NULL; + Limit = 0; + Xpand = false; + Usedom = false; + } // end of XMLDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) + { + char *defrow, *defcol, buf[10]; +//void *memp = Cat->GetDescp(); +//PSZ dbfile = Cat->GetDescFile(); + + Fn = Cat->GetStringCatInfo(g, "Filename", NULL); + Encoding = Cat->GetStringCatInfo(g, "Encoding", "UTF-8"); + + if (*Fn == '?') { + strcpy(g->Message, MSG(MISSING_FNAME)); + return true; + } // endif fn + + if ((signed)Cat->GetIntCatInfo("Flag", -1) != -1) { + strcpy(g->Message, MSG(DEPREC_FLAG)); + return true; + } // endif flag + + defrow = defcol = ""; + Cat->GetCharCatInfo("Coltype", "", buf, sizeof(buf)); + + switch (toupper(*buf)) { + case 'A': // Attribute + case '@': + case '0': + Coltype = 0; + break; + case '\0': // Default + case 'T': // Tag + case 'N': // Node + case '1': + Coltype = 1; + break; + case 'C': // Column + case 'P': // Position + case 'H': // HTML + case '2': + Coltype = 2; + defrow = "TR"; + defcol = "TD"; + break; + default: + sprintf(g->Message, MSG(INV_COL_TYPE), buf); + return true; + } // endswitch typname + + Tabname = Cat->GetStringCatInfo(g, "Name", Name); // Deprecated + Tabname = Cat->GetStringCatInfo(g, "Table_name", Tabname); + Rowname = Cat->GetStringCatInfo(g, "Rownode", defrow); + Colname = Cat->GetStringCatInfo(g, "Colnode", defcol); + Mulnode = Cat->GetStringCatInfo(g, "Mulnode", ""); + XmlDB = Cat->GetStringCatInfo(g, "XmlDB", ""); + Nslist = Cat->GetStringCatInfo(g, "Nslist", ""); + DefNs = Cat->GetStringCatInfo(g, "DefNs", ""); + Limit = Cat->GetIntCatInfo("Limit", 2); + Xpand = (Cat->GetIntCatInfo("Expand", 0) != 0); + Header = Cat->GetIntCatInfo("Header", 0); + Cat->GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf)); + +//if (*buf == '*') // Try the old (deprecated) option +// Cat->GetCharCatInfo("Method", "*", buf, sizeof(buf)); + +//if (*buf == '*') // Is there a default for the database? +// Cat->GetCharCatInfo("Defxml", XMLSUP, buf, sizeof(buf)); + + // Note that if no support is specified, the default is MS-DOM + // on Windows and libxml2 otherwise + if (*buf == '*') +#if defined(WIN32) + Usedom = true; +#else // !WIN32 + Usedom = false; +#endif // !WIN32 + else + Usedom = (toupper(*buf) == 'M' || toupper(*buf) == 'D'); + + // Get eventual table node attribute + Attrib = Cat->GetStringCatInfo(g, "Attribute", ""); + Hdattr = Cat->GetStringCatInfo(g, "HeadAttr", ""); + + return false; + } // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new TDB of the proper type. */ +/***********************************************************************/ +PTDB XMLDEF::GetTable(PGLOBAL g, MODE m) + { + return new(g) TDBXML(this); + } // end of GetTable + +/***********************************************************************/ +/* DeleteTableFile: Delete XML table files using platform API. */ +/***********************************************************************/ +bool XMLDEF::DeleteTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + bool rc; + + // Delete the XML table file if not protected + if (!IsReadOnly()) { + PlugSetPath(filename, Fn, GetPath()); +#if defined(WIN32) + rc = !DeleteFile(filename); +#else // UNIX + rc = remove(filename); +#endif // UNIX + } else + rc =true; + + return rc; // Return true if error + } // end of DeleteTableFile + +/* ------------------------- TDBXML Class ---------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBXML constuctor. */ +/***********************************************************************/ +TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp) + { + Docp = NULL; + Root = NULL; + Curp = NULL; + DBnode = NULL; + TabNode = NULL; + RowNode = NULL; + ColNode = NULL; + Nlist = NULL; + Clist = NULL; + To_Xb = NULL; + Colp = NULL; + Xfile = tdp->Fn; + Enc = tdp->Encoding; + Tabname = tdp->Tabname; + Rowname = (*tdp->Rowname) ? tdp->Rowname : NULL; + Colname = (*tdp->Colname) ? tdp->Colname : NULL; + Mulnode = (*tdp->Mulnode) ? tdp->Mulnode : NULL; + XmlDB = (*tdp->XmlDB) ? tdp->XmlDB : NULL; + Nslist = (*tdp->Nslist) ? tdp->Nslist : NULL; + DefNs = (*tdp->DefNs) ? tdp->DefNs : NULL; + Attrib = (*tdp->Attrib) ? tdp->Attrib : NULL; + Hdattr = (*tdp->Hdattr) ? tdp->Hdattr : NULL; + Coltype = tdp->Coltype; + Limit = tdp->Limit; + Xpand = tdp->Xpand; + Changed = false; + Checked = false; + NextSame = false; + NewRow = false; + Hasnod = false; + Write = false; + Bufdone = false; + Nodedone = false; + Void = false; + Usedom = tdp->Usedom; + Header = tdp->Header; + Nrow = -1; + Irow = Header - 1; + Nsub = 0; + N = 0; + } // end of TDBXML constructor + +TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp) + { + Docp = tdbp->Docp; + Root = tdbp->Root; + Curp = tdbp->Curp; + DBnode = tdbp->DBnode; + TabNode = tdbp->TabNode; + RowNode = tdbp->RowNode; + ColNode = tdbp->ColNode; + Nlist = tdbp->Nlist; + Clist = tdbp->Clist; + To_Xb = tdbp->To_Xb; + Colp = tdbp->Colp; + Xfile = tdbp->Xfile; + Enc = tdbp->Enc; + Tabname = tdbp->Tabname; + Rowname = tdbp->Rowname; + Colname = tdbp->Colname; + Mulnode = tdbp->Mulnode; + XmlDB = tdbp->XmlDB; + Nslist = tdbp->Nslist; + DefNs = tdbp->DefNs; + Attrib = tdbp->Attrib; + Hdattr = tdbp->Hdattr; + Coltype = tdbp->Coltype; + Limit = tdbp->Limit; + Xpand = tdbp->Xpand; + Changed = tdbp->Changed; + Checked = tdbp->Checked; + NextSame = tdbp->NextSame; + NewRow = tdbp->NewRow; + Hasnod = tdbp->Hasnod; + Write = tdbp->Write; + Void = tdbp->Void; + Usedom = tdbp->Usedom; + Header = tdbp->Header; + Nrow = tdbp->Nrow; + Irow = tdbp->Irow; + Nsub = tdbp->Nsub; + N = tdbp->N; + } // end of TDBXML copy constructor + +// Used for update +PTDB TDBXML::CopyOne(PTABS t) + { + PTDB tp; + PXMLCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBXML(this); + + for (cp1 = (PXMLCOL)Columns; cp1; cp1 = (PXMLCOL)cp1->GetNext()) { + cp2 = new(g) XMLCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate XML column description block. */ +/***********************************************************************/ +PCOL TDBXML::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + if (trace) + htrc("TDBXML: MakeCol %s n=%d\n", (cdp) ? cdp->GetName() : "<null>", n); + + return new(g) XMLCOL(cdp, this, cprec, n); + } // end of MakeCol + +/***********************************************************************/ +/* InsertSpecialColumn: Put a special column ahead of the column list.*/ +/***********************************************************************/ +PCOL TDBXML::InsertSpecialColumn(PGLOBAL g, PCOL colp) + { + if (!colp->IsSpecial()) + return NULL; + +//if (Xpand && ((SPCBLK*)colp)->GetRnm()) +// colp->SetKey(0); // Rownum is no more a key + + colp->SetNext(Columns); + Columns = colp; + return colp; + } // end of InsertSpecialColumn + +/***********************************************************************/ +/* LoadTableFile: Load and parse an XML file. */ +/***********************************************************************/ +int TDBXML::LoadTableFile(PGLOBAL g) + { + char filename[_MAX_PATH]; + int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2; + PFBLOCK fp = NULL; + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* We used the file name relative to recorded datapath. */ + /*********************************************************************/ + PlugSetPath(filename, Xfile, GetPath()); + + if (trace) + htrc("TDBXML: loading %s\n", filename); + + /*********************************************************************/ + /* Firstly we check whether this file have been already loaded. */ + /*********************************************************************/ + if (Mode == MODE_READ) + for (fp = dup->Openlist; fp; fp = fp->Next) + if (fp->Type == type && fp->Length && fp->Count) + if (!stricmp(fp->Fname, filename)) + break; + + if (fp) { + /*******************************************************************/ + /* File already loaded. Just increment use count and get pointer. */ + /*******************************************************************/ + fp->Count++; + Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc, fp) + : GetLibxmlDoc(g, Nslist, DefNs, Enc, fp); + } else { + /*******************************************************************/ + /* Parse the XML file. */ + /*******************************************************************/ + if (!(Docp = (Usedom) ? GetDomDoc(g, Nslist, DefNs, Enc) + : GetLibxmlDoc(g, Nslist, DefNs, Enc))) + return RC_FX; + + // Initialize the implementation + if (Docp->Initialize(g)) { + sprintf(g->Message, MSG(INIT_FAILED), (Usedom) ? "DOM" : "libxml2"); + return RC_FX; + } // endif init + + if (trace) + htrc("TDBXML: parsing %s rc=%d\n", filename, rc); + + // Parse the XML file + if (Docp->ParseFile(filename)) { + // Does the file exist? + int h= global_open(g, MSGID_NONE, filename, _O_RDONLY); + + if (h != -1) { + rc = (!_filelength(h)) ? RC_EF : RC_INFO; + close(h); + } else + rc = (errno == ENOENT) ? RC_NF : RC_INFO; + + return rc; + } // endif Docp + + /*******************************************************************/ + /* Link a Xblock. This make possible to reuse already opened docs */ + /* and also to automatically close them in case of error g->jump. */ + /*******************************************************************/ + fp = Docp->LinkXblock(g, Mode, rc, filename); + } // endif xp + + To_Xb = fp; // Useful when closing + return rc; + } // end of LoadTableFile + +/***********************************************************************/ +/* Initialize the processing of the XML file. */ +/* Note: this function can be called several times, eventally before */ +/* the columns are known (from TBL for instance) */ +/***********************************************************************/ +bool TDBXML::Initialize(PGLOBAL g) + { + char tabpath[64]; + int rc; + PXMLCOL colp; + + if (Void) + return false; + + if (Columns && !Bufdone) { + // Allocate the buffers that will contain node values + for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext()) + if (!colp->IsSpecial()) // Not a pseudo column + if (colp->AllocBuf(g, Mode == MODE_INSERT)) + return true; + + Bufdone = true; + } // endif Bufdone + +#if !defined(UNIX) + if (!Root) try { +#else + if (!Root) { +#endif + // Load or re-use the table file + rc = LoadTableFile(g); + + if (rc == RC_OK) { + // Get root node + if (!(Root = Docp->GetRoot(g))) { + // This should never happen as load should have failed + strcpy(g->Message, MSG(EMPTY_DOC)); + goto error; + } // endif Root + + // If tabname is not an Xpath, + // construct one that will find it anywhere + if (!strchr(Tabname, '/')) + strcat(strcpy(tabpath, "//"), Tabname); + else + strcpy(tabpath, Tabname); + + // Evaluate table xpath + if ((TabNode = Root->SelectSingleNode(g, tabpath))) { + if (TabNode->GetType() != XML_ELEMENT_NODE) { + sprintf(g->Message, MSG(BAD_NODE_TYPE), TabNode->GetType()); + goto error; + } // endif Type + + } else if (Mode == MODE_INSERT && XmlDB) { + // We are adding a new table to a multi-table file + + // If XmlDB is not an Xpath, + // construct one that will find it anywhere + if (!strchr(XmlDB, '/')) + strcat(strcpy(tabpath, "//"), XmlDB); + else + strcpy(tabpath, XmlDB); + + if (!(DBnode = Root->SelectSingleNode(g, tabpath))) { + // DB node does not exist yet; we cannot create it + // because we don't know where it should be placed + sprintf(g->Message, MSG(MISSING_NODE), XmlDB, Xfile); + goto error; + } // endif DBnode + + if (!(TabNode = DBnode->AddChildNode(g, Tabname))) { + sprintf(g->Message, MSG(FAIL_ADD_NODE), Tabname); + goto error; + } // endif TabNode + + DBnode->AddText(g, "\n"); + } else + TabNode = Root; // Try this ? + + } else if (rc == RC_NF || rc == RC_EF) { + // The XML file does not exist or is void + if (Mode == MODE_INSERT) { + // New Document + char buf[64]; + + // Create the XML node + if (Docp->NewDoc(g, "1.0")) { + strcpy(g->Message, MSG(NEW_DOC_FAILED)); + goto error; + } // endif NewDoc + + // Add a CONNECT comment node +// sprintf(buf, MSG(CREATED_PLUGDB), version); + sprintf(buf, " Created by CONNECT %s ", version); + Docp->AddComment(g, buf); + + if (XmlDB) { + // This is a multi-table file + DBnode = Root = Docp->NewRoot(g, XmlDB); + DBnode->AddText(g, "\n"); + TabNode = DBnode->AddChildNode(g, Tabname); + DBnode->AddText(g, "\n"); + } else + TabNode = Root = Docp->NewRoot(g, Tabname); + + if (TabNode == NULL || Root == NULL) { + strcpy(g->Message, MSG(XML_INIT_ERROR)); + goto error; + } else if (SetTabNode(g)) + goto error; + + } else { + sprintf(g->Message, MSG(FILE_UNFOUND), Xfile); + + if (Mode == MODE_READ) { + PushWarning(g, this); + Void = true; + } // endif Mode + + goto error; + } // endif Mode + + } else if (rc == RC_INFO) { + // Loading failed + sprintf(g->Message, MSG(LOADING_FAILED), Xfile); + goto error; + } else // (rc == RC_FX) + goto error; + + // Get row node list + if (Rowname) + Nlist = TabNode->SelectNodes(g, Rowname); + else + Nlist = TabNode->GetChildElements(g); + +#if defined(WIN32) + } catch(_com_error e) { + // We come here if a DOM command threw an error + char buf[128]; + + rc = WideCharToMultiByte(CP_ACP, 0, e.Description(), -1, + buf, sizeof(buf), NULL, NULL); + + if (rc) + sprintf(g->Message, "%s: %s", MSG(COM_ERROR), buf); + else + sprintf(g->Message, "%s hr=%p", MSG(COM_ERROR), e.Error()); + + goto error; +#endif // WIN32 +#if !defined(UNIX) + } catch(...) { + // Other errors + strcpy(g->Message, MSG(XMLTAB_INIT_ERR)); + goto error; +#endif + } // end of try-catches + + if (Root && Columns && !Nodedone) { + // Allocate class nodes to avoid dynamic allocation + for (colp = (PXMLCOL)Columns; colp; colp = (PXMLCOL)colp->GetNext()) + if (!colp->IsSpecial()) // Not a pseudo column + colp->AllocNodes(g, Docp); + + Nodedone = true; + } // endif Nodedone + + if (Nrow < 0) + Nrow = (Nlist) ? Nlist->GetLength() : 0; + + // Init is Ok + return false; + +error: + if (Docp) + Docp->CloseDoc(g, To_Xb); + + return !Void; +} // end of Initialize + +/***********************************************************************/ +/* Set TabNode attributes or header. */ +/***********************************************************************/ +bool TDBXML::SetTabNode(PGLOBAL g) + { + assert(Mode == MODE_INSERT); + + if (Attrib) + SetNodeAttr(g, Attrib, TabNode); + + if (Header) { + PCOLDEF cdp; + PXNODE rn, cn; + + if (Rowname) { + TabNode->AddText(g, "\n\t"); + rn = TabNode->AddChildNode(g, Rowname, NULL); + } else { + strcpy(g->Message, MSG(NO_ROW_NODE)); + return true; + } // endif Rowname + + if (Hdattr) + SetNodeAttr(g, Hdattr, rn); + + for (cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) { + rn->AddText(g, "\n\t\t"); + cn = rn->AddChildNode(g, "TH", NULL); + cn->SetContent(g, (char *)cdp->GetName(), + strlen(cdp->GetName()) + 1); + } // endfor cdp + + rn->AddText(g, "\n\t"); + } // endif ColType + + return false; + } // end of SetTabNode + +/***********************************************************************/ +/* Set attributes of a table or header node. */ +/***********************************************************************/ +void TDBXML::SetNodeAttr(PGLOBAL g, char *attr, PXNODE node) + { + char *p, *pa, *pn = attr; + PXATTR an; + + do { + if ((p = strchr(pn, '='))) { + pa = pn; + *p++ = 0; + + if ((pn = strchr(p, ';'))) + *pn++ = 0; + + an = node->AddProperty(g, pa, NULL); + an->SetText(g, p, strlen(p) + 1); + } else + break; + + } while (pn); + + } // end of SetNodeAttr + +/***********************************************************************/ +/* XML Cardinality: returns table cardinality in number of rows. */ +/* This function can be called with a null argument to test the */ +/* availability of Cardinality implementation (1 yes, 0 no). */ +/***********************************************************************/ +int TDBXML::Cardinality(PGLOBAL g) + { + if (!g) + return (Xpand || Coltype == 2) ? 0 : 1; + + if (Nrow < 0) + if (Initialize(g)) + return -1; + + return (Void) ? 0 : Nrow - Header; + } // end of Cardinality + +/***********************************************************************/ +/* XML GetMaxSize: returns the number of tables in the database. */ +/***********************************************************************/ +int TDBXML::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) + MaxSize = Cardinality(g) * ((Xpand) ? Limit : 1); + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* Return the position in the table. */ +/***********************************************************************/ +int TDBXML::GetRecpos(void) + { + union { + uint Rpos; + BYTE Spos[4]; + }; + + Rpos = htonl(Irow); + Spos[0] = (BYTE)Nsub; + return Rpos; + } // end of GetRecpos + +/***********************************************************************/ +/* RowNumber: return the ordinal number of the current row. */ +/***********************************************************************/ +int TDBXML::RowNumber(PGLOBAL g, bool b) + { + if (To_Kindex && (Xpand || Coltype == 2) && !b) { + /*******************************************************************/ + /* Don't know how to retrieve RowID for expanded XML tables. */ + /*******************************************************************/ + sprintf(g->Message, MSG(NO_ROWID_FOR_AM), + GetAmName(g, GetAmType())); + return 0; // Means error + } else + return (b || !(Xpand || Coltype == 2)) ? Irow - Header + 1 : N; + + } // end of RowNumber + +/***********************************************************************/ +/* XML Access Method opening routine. */ +/***********************************************************************/ +bool TDBXML::OpenDB(PGLOBAL g) + { + if (Use == USE_OPEN) { + /*******************************************************************/ + /* Table already open replace it at its beginning. */ + /*******************************************************************/ + if (!To_Kindex) { + Irow = Header - 1; + Nsub = 0; + } else + /*****************************************************************/ + /* Table is to be accessed through a sorted index table. */ + /*****************************************************************/ + To_Kindex->Reset(); + + return false; + } // endif use + + /*********************************************************************/ + /* OpenDB: initialize the XML file processing. */ + /*********************************************************************/ + Write = (Mode == MODE_INSERT || Mode == MODE_UPDATE); + + if (Initialize(g)) + return true; + + NewRow = (Mode == MODE_INSERT); + Nsub = 0; + Use = USE_OPEN; // Do it now in case we are recursively called + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for XML access method. */ +/***********************************************************************/ +int TDBXML::ReadDB(PGLOBAL g) + { + bool same; + + if (Void) + return RC_EF; + + /*********************************************************************/ + /* Now start the pseudo reading process. */ + /*********************************************************************/ + if (To_Kindex) { + /*******************************************************************/ + /* Reading is by an index table. */ + /*******************************************************************/ + union { + uint Rpos; + BYTE Spos[4]; + }; + + int recpos = To_Kindex->Fetch(g); + + switch (recpos) { + case -1: // End of file reached + return RC_EF; + case -2: // No match for join + return RC_NF; + case -3: // Same record as last non null one + same = true; + return RC_OK; + default: + Rpos = recpos; + Nsub = Spos[0]; + Spos[0] = 0; + + if (Irow != (signed)ntohl(Rpos)) { + Irow = ntohl(Rpos); + same = false; + } else + same = true; + + } // endswitch recpos + + } else { + if (trace) + htrc("TDBXML ReadDB: Irow=%d Nrow=%d\n", Irow, Nrow); + + // This is to force the table to be expanded when constructing + // an index for which the expand column is not specified. + if (Colp && Irow >= Header) { + Colp->Eval(g); + Colp->Reset(); + } // endif Colp + + if (!NextSame) { + if (++Irow == Nrow) + return RC_EF; + + same = false; + Nsub = 0; + } else { + // Not sure the multiple column read will be called + NextSame = false; + same = true; + Nsub++; + } // endif NextSame + + N++; // RowID + } // endif To_Kindex + + if (!same) { + if (trace > 1) + htrc("TDBXML ReadDB: Irow=%d RowNode=%p\n", Irow, RowNode); + + // Get the new row node + if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) { + sprintf(g->Message, MSG(MISSING_ROWNODE), Irow); + return RC_FX; + } // endif RowNode + + if (Colname && Coltype == 2) + Clist = RowNode->SelectNodes(g, Colname, Clist); + + } // endif same + + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* CheckRow: called On Insert and Update. Must create the Row node */ +/* if it does not exist (Insert) and update the Clist if called by */ +/* a column having an Xpath because it can use an existing node that */ +/* was added while inserting or Updating this row. */ +/***********************************************************************/ +bool TDBXML::CheckRow(PGLOBAL g, bool b) + { + if (NewRow && Mode == MODE_INSERT) + if (Rowname) { + TabNode->AddText(g, "\n\t"); + RowNode = TabNode->AddChildNode(g, Rowname, RowNode); + } else { + strcpy(g->Message, MSG(NO_ROW_NODE)); + return true; + } // endif Rowname + + if (Colname && (NewRow || b)) + Clist = RowNode->SelectNodes(g, Colname, Clist); + + return NewRow = false; + } // end of CheckRow + +/***********************************************************************/ +/* WriteDB: Data Base write routine for XDB access methods. */ +/***********************************************************************/ +int TDBXML::WriteDB(PGLOBAL g) + { + if (Mode == MODE_INSERT) { + if (Hasnod) + RowNode->AddText(g, "\n\t"); + + NewRow = true; + } // endif Mode + + // Something was changed in the document + Changed = true; + return RC_OK; + } // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for XDB access methods. */ +/***********************************************************************/ +int TDBXML::DeleteDB(PGLOBAL g, int irc) + { + if (irc == RC_FX) { + // Delete all rows + for (Irow = 0; Irow < Nrow; Irow++) + if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) { + sprintf(g->Message, MSG(MISSING_ROWNODE), Irow); + return RC_FX; + } else + TabNode->DeleteChild(g, RowNode); + + Changed = true; + } else if (irc != RC_EF) { + TabNode->DeleteChild(g, RowNode); + Changed = true; + } // endif's irc + + return RC_OK; + } // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for XDB access methods. */ +/***********************************************************************/ +void TDBXML::CloseDB(PGLOBAL g) + { + if (Docp) { + if (Changed) { + char filename[_MAX_PATH]; +// PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + // We used the file name relative to recorded datapath + PlugSetPath(filename, Xfile, GetPath()); + + if (Mode == MODE_INSERT) + TabNode->AddText(g, "\n"); + + // Save the modified document + if (Docp->DumpDoc(g, filename)) { + PushWarning(g, this); + Docp->CloseDoc(g, To_Xb); + + // This causes a crash in Diagnostics_area::set_error_status +// longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif DumpDoc + + } // endif Changed + + // Free the document and terminate XML processing + Docp->CloseDoc(g, To_Xb); + } // endif docp + + } // end of CloseDB + +// ------------------------ XMLCOL functions ---------------------------- + +/***********************************************************************/ +/* XMLCOL public constructor. */ +/***********************************************************************/ +XMLCOL::XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) + { + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + // Set additional XML access method information for column. + Tdbp = (PTDBXML)tdbp; + Nl = NULL; + Nlx = NULL; + ColNode = NULL; + ValNode = NULL; + Cxnp = NULL; + Vxnp = NULL; + Vxap = NULL; + AttNode = NULL; + Nodes = NULL; + Nod = 0; + Inod = -1; + Mul = false; + Checked = false; + Xname = cdp->GetFmt(); + Long = cdp->GetLong(); + Rank = cdp->GetOffset(); + Type = Tdbp->Coltype; + Nx = -1; + Sx = -1; + Valbuf = NULL; + To_Val = NULL; + } // end of XMLCOL constructor + +/***********************************************************************/ +/* XMLCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) + { + Tdbp = col1->Tdbp; + Nl = col1->Nl; + Nlx = col1->Nlx; + ColNode = col1->ColNode; + ValNode = col1->ValNode; + Cxnp = col1->Cxnp; + Vxnp = col1->Vxnp; + Vxap = col1->Vxap; + AttNode = col1->AttNode; + Nodes = col1->Nodes; + Nod = col1->Nod; + Inod = col1->Inod; + Mul = col1->Mul; + Checked = col1->Checked; + Xname = col1->Xname; + Valbuf = col1->Valbuf; + Long = col1->Long; + Rank = col1->Rank; + Nx = col1->Nx; + Sx = col1->Sx; + Type = col1->Type; + To_Val = col1->To_Val; + } // end of XMLCOL copy constructor + +/***********************************************************************/ +/* Allocate a buffer of the proper size. */ +/***********************************************************************/ +bool XMLCOL::AllocBuf(PGLOBAL g, bool mode) + { + if (Valbuf) + return false; // Already done + + Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1); + Valbuf[Long] = '\0'; + return ParseXpath(g, mode); + } // end of AllocBuf + +/***********************************************************************/ +/* Parse the eventual passed Xpath information. */ +/* This information can be specified in the Xpath (or Fieldfmt) */ +/* column option when creating the table. It permits to indicate the */ +/* position of the node corresponding to that column in a Xpath-like */ +/* language (but not a truly compliant one). */ +/***********************************************************************/ +bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) + { + char *p, *p2, *pbuf = NULL; + int i, len = strlen(Name); + + len += ((Tdbp->Colname) ? strlen(Tdbp->Colname) : 0); + len += ((Xname) ? strlen(Xname) : 0); + pbuf = (char*)PlugSubAlloc(g, NULL, len + 3); + *pbuf = '\0'; + + if (!mode) + // Take care of an eventual extra column node a la html + if (Tdbp->Colname) { + sprintf(pbuf, Tdbp->Colname, Rank + ((Tdbp->Usedom) ? 0 : 1)); + strcat(pbuf, "/"); + } // endif Colname + + if (Xname) { + if (Type == 2) { + sprintf(g->Message, MSG(BAD_COL_XPATH), Name, Tdbp->Name); + return true; + } else + strcat(pbuf, Xname); + + if (trace) + htrc("XMLCOL: pbuf=%s\n", pbuf); + + // For Update or Insert the Xpath must be analyzed + if (mode) { + for (i = 0, p = pbuf; (p = strchr(p, '/')); i++, p++) + Nod++; // One path node found + + if (Nod) + Nodes = (char**)PlugSubAlloc(g, NULL, Nod * sizeof(char*)); + + } // endif mode + + // Analyze the Xpath for this column + for (i = 0, p = pbuf; (p2 = strchr(p, '/')); i++, p = p2 + 1) { + if (Tdbp->Mulnode && !strncmp(p, Tdbp->Mulnode, p2 - p)) + if (!Tdbp->Xpand && mode) { + strcpy(g->Message, MSG(CONCAT_SUBNODE)); + return true; + } else + Inod = i; // Index of multiple node + + if (mode) { + // For Update or Insert the Xpath must be explicit + if (strchr("@/.*", *p)) { + sprintf(g->Message, MSG(XPATH_NOT_SUPP), Name); + return true; + } else + Nodes[i] = p; + + *p2 = '\0'; + } // endif mode + + } // endfor i, p + + if (*p == '/' || *p == '.') { + sprintf(g->Message, MSG(XPATH_NOT_SUPP), Name); + return true; + } else if (*p == '@') { + p++; // Remove the @ if mode + Type = 0; // Column is an attribute + } else + Type = 1; // Column is a node + + if (!*p) + strcpy(p, Name); // Xname is column name + + if (Type && Tdbp->Mulnode && !strcmp(p, Tdbp->Mulnode)) + Inod = Nod; // Index of multiple node + + if (mode) // Prepare Xname + pbuf = p; + + } else if (Type == 2) { + // HTML like table, columns are retrieved by position + new(this) XPOSCOL(Value); // Change the class of this column + Tdbp->Hasnod = true; + return false; + } else if (Type == 0 && !mode) { + strcat(strcat(pbuf, "@"), Name); + } else { // Type == 1 + if (Tdbp->Mulnode && !strcmp(Name, Tdbp->Mulnode)) + Inod = 0; // Nod + + strcat(pbuf, Name); + } // endif,s + + if (Inod >= 0) { + Tdbp->Colp = this; // To force expand + new(this) XMULCOL(Value); // Change the class of this column + } // endif Inod + + if (Type || Nod) + Tdbp->Hasnod = true; + + if (trace) + htrc("XMLCOL: Xname=%s\n", pbuf); + + // Save the calculated Xpath + Xname = pbuf; + return false; + } // end of ParseXpath + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool XMLCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) + { + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_FLOAT) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetPrecision()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) { + To_Tdb = (PTDB)To_Tdb->GetOrig(); + Tdbp = (PTDBXML)To_Tdb; // Specific of XMLCOL + + // Allocate the XML buffer + if (AllocBuf(g, true)) // In Write mode + return true; + + } // endif GetOrig + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; + } // end of SetBuffer + +/***********************************************************************/ +/* Alloc the nodes that will be used during the whole process. */ +/***********************************************************************/ +void XMLCOL::AllocNodes(PGLOBAL g, PXDOC dp) +{ + Cxnp = dp->NewPnode(g); + Vxnp = dp->NewPnode(g); + Vxap = dp->NewPattr(g); +} // end of AllocNodes + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XMLCOL::ReadColumn(PGLOBAL g) + { + if (Nx == Tdbp->Irow) + return; // Same row than the last read + + ValNode = Tdbp->RowNode->SelectSingleNode(g, Xname, Vxnp); + + if (ValNode) { + if (ValNode->GetType() != XML_ELEMENT_NODE && + ValNode->GetType() != XML_ATTRIBUTE_NODE) { + sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif type + + // Get the Xname value from the XML file + switch (ValNode->GetContent(g, Valbuf, Long + 1)) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endswitch + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + Nx = Tdbp->Irow; + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last row of */ +/* the corresponding table, and rewrite the content corresponding */ +/* to this column node from the column buffer and type. */ +/***********************************************************************/ +void XMLCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int done = 0; + int i, n, k = 0; + PXNODE TopNode = NULL; +//PXATTR AttNode = NULL; + + if (trace) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked) { + assert (ColNode != NULL); + assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For columns having an Xpath, the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, Nod || Tdbp->Colname)) + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist) { + n = Tdbp->Clist->GetLength(); + ColNode = NULL; + } else { + n = 1; + ColNode = Tdbp->RowNode->Clone(g, ColNode); + } // endif Clist + + ValNode = NULL; + + for (i = 0; i < n; i++) { + if (Tdbp->Clist) + ColNode = Tdbp->Clist->GetItem(g, i, Cxnp); + + /*******************************************************************/ + /* Check whether an Xpath was provided to go to the column node. */ + /*******************************************************************/ + for (k = 0; k < Nod; k++) + if ((ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp))) + TopNode = ColNode; + else + break; + + if (ColNode) + if (Type) + ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp); + else + AttNode = ColNode->GetAttribute(g, Xname, Vxap); + + if (TopNode || ValNode || AttNode) + break; // We found the good column + else if (Tdbp->Clist) + ColNode = NULL; + + } // endfor i + + /*********************************************************************/ + /* Create missing nodes. */ + /*********************************************************************/ + if (ColNode == NULL) { + if (TopNode == NULL) + if (Tdbp->Clist) { + Tdbp->RowNode->AddText(g, "\n\t\t"); + ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname); + done = 2; + TopNode = ColNode; + } else + TopNode = Tdbp->RowNode; + + for (; k < Nod && TopNode; k++) { + if (!done) { + TopNode->AddText(g, "\n\t\t"); + done = 1; + } // endif done + + ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp); + TopNode = ColNode; + } // endfor k + + if (ColNode == NULL) { + strcpy(g->Message, MSG(COL_ALLOC_ERR)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif ColNode + + } // endif ColNode + + if (Type == 1) { + if (ValNode == NULL) { + if (done < 2) + ColNode->AddText(g, "\n\t\t"); + + ValNode = ColNode->AddChildNode(g, Xname, Vxnp); + } // endif ValNode + + } else // (Type == 0) + if (AttNode == NULL) + AttNode = ColNode->AddProperty(g, Xname, Vxap); + + if (ValNode == NULL && AttNode == NULL) { + strcpy(g->Message, MSG(VAL_ALLOC_ERR)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) { + if (Type) { + ValNode->SetContent(g, Valbuf, Long); + } else + AttNode->SetText(g, Valbuf, Long); + + } // endif Status + + } // end of WriteColumn + +// ------------------------ XMULCOL functions --------------------------- + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XMULCOL::ReadColumn(PGLOBAL g) + { + char *p; + int i, n, len; + + if (Nx != Tdbp->Irow) // New row + Nl = Tdbp->RowNode->SelectNodes(g, Xname, Nl); + else if (Sx == Tdbp->Nsub) + return; // Same row + + if ((n = Nl->GetLength())) { + *(p = Valbuf) = '\0'; + len = Long; + + for (i = Tdbp->Nsub; i < n; i++) { + ValNode = Nl->GetItem(g, i, Vxnp); + + if (ValNode->GetType() != XML_ELEMENT_NODE && + ValNode->GetType() != XML_ATTRIBUTE_NODE) { + sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif type + + // Get the Xname value from the XML file + switch (ValNode->GetContent(g, p, len + 1)) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endswitch + + if (!Tdbp->Xpand) { + // Concatenate all values + if (n - i > 1) + strncat(Valbuf, ", ", Long + 1); + + len -= strlen(p); + p += strlen(p); + } else + break; + + } // endfor i + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + Nx = Tdbp->Irow; + Sx = Tdbp->Nsub; + Tdbp->NextSame = (Tdbp->Xpand && Nl->GetLength() - Sx > 1); + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void XMULCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int done = 0; + int i, n, len, k = 0; + PXNODE TopNode = NULL; +//PXATTR AttNode = NULL; + + if (trace) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked) { + assert (ColNode); + assert ((Type ? (void *)ValNode : (void *)AttNode) != NULL); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For columns having an Xpath, the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, Nod)) + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist) { + n = Tdbp->Clist->GetLength(); + ColNode = NULL; + } else { + n = 1; + ColNode = Tdbp->RowNode->Clone(g, ColNode); + } // endif Clist + + ValNode = NULL; + + for (i = 0; i < n; i++) { + if (Tdbp->Clist) + ColNode = Tdbp->Clist->GetItem(g, i, Cxnp); + + /*******************************************************************/ + /* Check whether an Xpath was provided to go to the column node. */ + /*******************************************************************/ + for (k = 0; k < Nod; k++) { + if (k == Inod) { + // This is the multiple node + Nlx = ColNode->SelectNodes(g, Nodes[k], Nlx); + ColNode = Nlx->GetItem(g, Tdbp->Nsub, Cxnp); + } else + ColNode = ColNode->SelectSingleNode(g, Nodes[k], Cxnp); + + if (ColNode == NULL) + break; + + TopNode = ColNode; + } // endfor k + + if (ColNode) + if (Inod == Nod) { + /***************************************************************/ + /* The node value can be multiple. */ + /***************************************************************/ + assert (Type); + + // Get the value Node from the XML list + Nlx = ColNode->SelectNodes(g, Xname, Nlx); + len = Nlx->GetLength(); + + if (len > 1 && !Tdbp->Xpand) { + sprintf(g->Message, MSG(BAD_VAL_UPDATE), Name); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } else + ValNode = Nlx->GetItem(g, Tdbp->Nsub, Vxnp); + + } else // Inod != Nod + if (Type) + ValNode = ColNode->SelectSingleNode(g, Xname, Vxnp); + else + AttNode = ColNode->GetAttribute(g, Xname, Vxap); + + if (TopNode || ValNode || AttNode) + break; // We found the good column + else if (Tdbp->Clist) + ColNode = NULL; + + } // endfor i + + /*********************************************************************/ + /* Create missing nodes. */ + /*********************************************************************/ + if (ColNode == NULL) { + if (TopNode == NULL) + if (Tdbp->Clist) { + Tdbp->RowNode->AddText(g, "\n\t\t"); + ColNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname); + done = 2; + TopNode = ColNode; + } else + TopNode = Tdbp->RowNode; + + for (; k < Nod && TopNode; k++) { + if (!done) { + TopNode->AddText(g, "\n\t\t"); + done = 1; + } // endif done + + ColNode = TopNode->AddChildNode(g, Nodes[k], Cxnp); + TopNode = ColNode; + } // endfor k + + if (ColNode == NULL) { + strcpy(g->Message, MSG(COL_ALLOC_ERR)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif ColNode + + } // endif ColNode + + if (Type == 1) { + if (ValNode == NULL) { + if (done < 2) + ColNode->AddText(g, "\n\t\t"); + + ValNode = ColNode->AddChildNode(g, Xname, Vxnp); + } // endif ValNode + + } else // (Type == 0) + if (AttNode == NULL) + AttNode = ColNode->AddProperty(g, Xname, Vxap); + + if (ValNode == NULL && AttNode == NULL) { + strcpy(g->Message, MSG(VAL_ALLOC_ERR)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) { + if (Type) { + ValNode->SetContent(g, Valbuf, Long); + } else + AttNode->SetText(g, Valbuf, Long); + + } // endif Status + + } // end of WriteColumn + +/* ------------------------ XPOSCOL functions ------------------------ */ + +/***********************************************************************/ +/* ReadColumn: what this routine does is to access the column node */ +/* from the corresponding table, extract from it the node text and */ +/* convert it to the column type. */ +/***********************************************************************/ +void XPOSCOL::ReadColumn(PGLOBAL g) + { + if (Nx == Tdbp->Irow) + return; // Same row than the last read + + if (Tdbp->Clist == NULL) { + strcpy(g->Message, MSG(MIS_TAG_LIST)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif Clist + + if ((ValNode = Tdbp->Clist->GetItem(g, Rank, Vxnp))) { + // Get the column value from the XML file + switch (ValNode->GetContent(g, Valbuf, Long + 1)) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endswitch + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); + + Value->Reset(); // Null value + } // endif ValNode + + Nx = Tdbp->Irow; + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: what this routine does is to access the last line */ +/* read from the corresponding table, and rewrite the field */ +/* corresponding to this column from the column buffer and type. */ +/***********************************************************************/ +void XPOSCOL::WriteColumn(PGLOBAL g) + { + char *p, buf[16]; + int i, k, n; + + if (trace) + htrc("XML WriteColumn: col %s R%d coluse=%.4X status=%.4X\n", + Name, Tdbp->GetTdb_No(), ColUse, Status); + + /*********************************************************************/ + /* Check whether this node must be written. */ + /*********************************************************************/ + if (Value != To_Val) + Value->SetValue_pval(To_Val, false); // Convert the updated value + + if (Value->IsNull()) + return; + + /*********************************************************************/ + /* If a check pass was done while updating, all node contruction */ + /* has been already one. */ + /*********************************************************************/ + if (Status && Tdbp->Checked) { + assert (ValNode); + goto fin; + } // endif Checked + + /*********************************************************************/ + /* On Insert, a Row node must be created for each row; */ + /* For all columns the Clist must be updated. */ + /*********************************************************************/ + if (Tdbp->CheckRow(g, true)) + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + + /*********************************************************************/ + /* Find the column and value nodes to update or insert. */ + /*********************************************************************/ + if (Tdbp->Clist == NULL) { + strcpy(g->Message, MSG(MIS_TAG_LIST)); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endif Clist + + n = Tdbp->Clist->GetLength(); + k = Rank; + + if (!(ValNode = Tdbp->Clist->GetItem(g, k, Vxnp))) { + /*******************************************************************/ + /* Create missing column nodes. */ + /*******************************************************************/ + Tdbp->RowNode->AddText(g, "\n\t\t"); + + for (i = n; i <= k; i++) + ValNode = Tdbp->RowNode->AddChildNode(g, Tdbp->Colname, Vxnp); + + assert (ValNode); + } // endif ValNode + + /*********************************************************************/ + /* Get the string representation of Value according to column type. */ + /*********************************************************************/ + p = Value->GetCharString(buf); + + if (strlen(p) > (unsigned)Long) { + sprintf(g->Message, MSG(VALUE_TOO_LONG), p, Name, Long); + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } else + strcpy(Valbuf, p); + + /*********************************************************************/ + /* Updating must be done only when not in checking pass. */ + /*********************************************************************/ + fin: + if (Status) + ValNode->SetContent(g, Valbuf, Long); + + } // end of WriteColumn + +/* ------------------------ End of Tabxml ---------------------------- */ diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h new file mode 100644 index 00000000000..0f92d7a9bdc --- /dev/null +++ b/storage/connect/tabxml.h @@ -0,0 +1,246 @@ + +/*************** Tabxml H Declares Source Code File (.H) ***************/ +/* Name: TABXML.H Version 1.6 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2007-2013 */ +/* */ +/* This file contains the XML table classes declares. */ +/***********************************************************************/ +#define TYPE_AM_XML (AMT)127 + +typedef class XMLDEF *PXMLDEF; +typedef class TDBXML *PTDBXML; +typedef class XMLCOL *PXMLCOL; + +// These functions are exported from the Extended.dll +//PTABDEF __stdcall GetXML(PGLOBAL g, void *memp); + +/* --------------------------- XML classes --------------------------- */ + +/***********************************************************************/ +/* XML table. */ +/***********************************************************************/ +class DllExport XMLDEF : public TABDEF { /* Logical table description */ + friend class TDBXML; + public: + // Constructor + XMLDEF(void); + + // Implementation + virtual const char *GetType(void) {return "XML";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + virtual bool DeleteTableFile(PGLOBAL g); + + protected: + // Members + char *Fn; /* Path/Name of corresponding file */ + char *Encoding; /* New XML table file encoding */ + char *Tabname; /* Name of Table node */ + char *Rowname; /* Name of first level nodes */ + char *Colname; /* Name of second level nodes */ + char *Mulnode; /* Name of multiple node */ + char *XmlDB; /* Name of XML DB node */ + char *Nslist; /* List of namespaces to register */ + char *DefNs; /* Dummy name of default namespace */ + char *Attrib; /* Table node attributes */ + char *Hdattr; /* Header node attributes */ + int Coltype; /* Default column type */ + int Limit; /* Limit of multiple values */ + int Header; /* n first rows are header rows */ + bool Xpand; /* Put multiple tags in several rows */ + bool Usedom; /* True: DOM, False: libxml2 */ + }; // end of XMLDEF + +#if defined(INCLUDE_TDBXML) + +/***********************************************************************/ +/* This is the class declaration for the simple XML tables. */ +/***********************************************************************/ +class DllExport TDBXML : public TDBASE { + friend class XMLCOL; + friend class XMULCOL; + friend class XPOSCOL; + public: + // Constructor + TDBXML(PXMLDEF tdp); + TDBXML(PTDBXML tdbp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_XML;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXML(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); + virtual int GetRecpos(void); + virtual int GetProgCur(void) {return N;} + virtual PSZ GetFile(PGLOBAL g) {return Xfile;} + virtual void SetFile(PGLOBAL g, PSZ fn) {Xfile = fn;} + virtual void ResetDB(void) {N = 0;} + virtual void ResetSize(void) {MaxSize = -1;} + virtual int RowNumber(PGLOBAL g, bool b = false); + int LoadTableFile(PGLOBAL g); + bool Initialize(PGLOBAL g); + bool SetTabNode(PGLOBAL g); + void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node); + bool CheckRow(PGLOBAL g, bool b); + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp); +//virtual int GetMaxSame(PGLOBAL g) {return (Xpand) ? Limit : 1;} + virtual int Cardinality(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); +//virtual bool NeedIndexing(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + virtual int CheckWrite(PGLOBAL g) {Checked = true; return 0;} + virtual const CHARSET_INFO *data_charset() + {return &my_charset_utf8_general_ci;} + + protected: + // Members + PXDOC Docp; + PXNODE Root; + PXNODE Curp; + PXNODE DBnode; + PXNODE TabNode; + PXNODE RowNode; + PXNODE ColNode; + PXLIST Nlist; + PXLIST Clist; + PFBLOCK To_Xb; // Pointer to XML file block + PCOL Colp; // The multiple column + bool Changed; // After Update, Insert or Delete + bool Checked; // After Update check pass + bool NextSame; // Same next row + bool Xpand; // Put multiple tags in several rows + bool NewRow; // True when inserting a new row + bool Hasnod; // True if rows have subnodes + bool Write; // True for Insert and Update + bool Usedom; // True for DOM, False for libxml2 + bool Bufdone; // True when column buffers allocated + bool Nodedone; // True when column nodes allocated + bool Void; // True if the file does not exist + char *Xfile; // The XML file + char *Enc; // New XML table file encoding + char *Tabname; // Name of Table node + char *Rowname; // Name of first level nodes + char *Colname; // Name of second level nodes + char *Mulnode; // Name of multiple node + char *XmlDB; // Name of XML DB node + char *Nslist; // List of namespaces to register + char *DefNs; // Dummy name of default namespace + char *Attrib; // Table node attribut(s) + char *Hdattr; // Header node attribut(s) + int Coltype; // Default column type + int Limit; // Limit of multiple values + int Header; // n first rows are header rows + int Nrow; // The table cardinality + int Irow; // The current row index + int Nsub; // The current subrow index + int N; // The current Rowid + }; // end of class TDBXML + +/***********************************************************************/ +/* Class XMLCOL: XDB table access method column descriptor. */ +/***********************************************************************/ +class XMLCOL : public COLBLK { + public: + // Constructors + XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "XML"); + XMLCOL(XMLCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_XML;} + virtual void SetTo_Val(PVAL valp) {To_Val = valp;} + bool ParseXpath(PGLOBAL g, bool mode); + + // Methods + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + bool AllocBuf(PGLOBAL g, bool mode); + void AllocNodes(PGLOBAL g, PXDOC dp); + + protected: +//xmlNodePtr SelectSingleNode(xmlNodePtr node, char *name); + + // Default constructor not to be used + XMLCOL(void) : COLBLK(1) {} + + // Members + PXLIST Nl; + PXLIST Nlx; + PXNODE ColNode; + PXNODE ValNode; + PXNODE Cxnp; + PXNODE Vxnp; + PXATTR Vxap; + PXATTR AttNode; + PTDBXML Tdbp; + char *Valbuf; // To the node value buffer + char *Xname; // The node or attribute name + char* *Nodes; // The intermediate nodes + int Type; // 0: Attribute, 1: Tag, 2: position + int Nod; // The number of intermediate nodes + int Inod; // Index of multiple node + int Rank; // Position + bool Mul; // true for multiple column + bool Checked; // Was checked while Updating + int Long; // Buffer length + int Nx; // The last read row + int Sx; // The last read sub-row + PVAL To_Val; // To value used for Update/Insert + }; // end of class XMLCOL + +/***********************************************************************/ +/* Derived class XMLCOLX: used to replace a multiple XMLCOL by the */ +/* derived class XMULCOL that has specialize read and write functions.*/ +/* Note: this works only if the members of the derived class are the */ +/* same than the ones of the original class (NO added members). */ +/***********************************************************************/ +class XMLCOLX : public XMLCOL { + public: + // Fake operator new used to change a filter into a derived filter + void * operator new(size_t size, PXMLCOL colp) {return colp;} +#if !defined(__BORLANDC__) + // Avoid warning C4291 by defining a matching dummy delete operator + void operator delete(void *, PXMLCOL) {} +#endif + }; // end of class XMLCOLX + +/***********************************************************************/ +/* Class XMULCOL: XML table access method multiple column descriptor. */ +/***********************************************************************/ +class XMULCOL : public XMLCOLX { + public: + // The constructor must restore Value because XOBJECT has a void + // constructor called by default that set Value to NULL + XMULCOL(PVAL valp) {Value = valp; Mul = true;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + }; // end of class XMULCOL + +/***********************************************************************/ +/* Class XPOSCOL: XML table column accessed by position. */ +/***********************************************************************/ +class XPOSCOL : public XMLCOLX { + public: + // The constructor must restore Value because XOBJECT has a void + // constructor called by default that set Value to NULL + XPOSCOL(PVAL valp) {Value = valp;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); + }; // end of class XPOSCOL + +#endif // INCLUDE_TDBXML diff --git a/storage/connect/user_connect.cc b/storage/connect/user_connect.cc new file mode 100644 index 00000000000..dbb3015ab06 --- /dev/null +++ b/storage/connect/user_connect.cc @@ -0,0 +1,155 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2012 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file user_connect.cc + + @brief + Implements the user_connect class. + + @details + To support multi_threading, each query creates and use a PlugDB "user" + that is a connection with its personnal memory allocation. + + @note + +*/ + +/****************************************************************************/ +/* Author: Olivier Bertrand -- bertrandop@gmail.com -- 2004-2012 */ +/****************************************************************************/ +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#define DONT_DEFINE_VOID +#define MYSQL_SERVER +#include "sql_class.h" +#undef OFFSET + +#define NOPARSE +#include "osutil.h" +#include "global.h" +#include "plgdbsem.h" +#include "user_connect.h" +#include "mycat.h" + +extern "C" char plgxini[]; +extern int xtrace; + +/****************************************************************************/ +/* Initialize the user_connect static member. */ +/****************************************************************************/ +PCONNECT user_connect::to_users= NULL; + +/****************************************************************************/ +/* CONNECT functions called externally. */ +/****************************************************************************/ +PGLOBAL CntExit(PGLOBAL g); + +/* -------------------------- class user_connect -------------------------- */ + +/****************************************************************************/ +/* Constructor. */ +/****************************************************************************/ +user_connect::user_connect(THD *thd, const char *dbn) +{ + thdp= thd; + next= NULL; + previous= NULL; + g= NULL; + last_query_id= 0; + count= 0; + + // Statistics + nrd= fnd= nfd= 0; + tb1= 0; +} // end of user_connect constructor + + +/****************************************************************************/ +/* Destructor. */ +/****************************************************************************/ +user_connect::~user_connect() +{ + // Terminate CONNECT and Plug-like environment, should return NULL + g= CntExit(g); +} // end of user_connect destructor + + +/****************************************************************************/ +/* Initialization. */ +/****************************************************************************/ +bool user_connect::user_init(PHC hc) +{ + // Initialize Plug-like environment + PACTIVITY ap= NULL; + PDBUSER dup= NULL; + + // Areasize= 64M because of VEC tables. Should be parameterisable + g= PlugInit(NULL, 67108864); + + // Check whether the initialization is complete + if (!g || !g->Sarea || PlugSubSet(g, g->Sarea, g->Sarea_Size) + || !(dup= PlgMakeUser(g))) { + if (g) + printf("%s\n", g->Message); + + int rc= PlugExit(g); + g= NULL; + free(dup); + return true; + } // endif g-> + + dup->Catalog= new MYCAT(hc); + + ap= new ACTIVITY; + memset(ap, 0, sizeof(ACTIVITY)); + strcpy(ap->Ap_Name, "CONNECT"); + g->Activityp= ap; + g->Activityp->Aptr= dup; + next= to_users; + to_users= this; + + if (next) + next->previous= this; + + last_query_id= thdp->query_id; + count= 1; + return false; +} // end of user_init + + +/****************************************************************************/ +/* Check whether we begin a new query and if so cleanup the previous one. */ +/****************************************************************************/ +bool user_connect::CheckCleanup(void) +{ + if (thdp->query_id > last_query_id) { + PlugCleanup(g, true); + PlugSubSet(g, g->Sarea, g->Sarea_Size); + g->Xchk = NULL; + g->Createas = 0; + last_query_id= thdp->query_id; + + if (xtrace) + printf("=====> Begin new query %llu\n", last_query_id); + + return true; + } // endif query_id + + return false; +} // end of CheckCleanup + diff --git a/storage/connect/user_connect.h b/storage/connect/user_connect.h new file mode 100644 index 00000000000..e4498912be5 --- /dev/null +++ b/storage/connect/user_connect.h @@ -0,0 +1,79 @@ +/* Copyright (C) Olivier Bertrand 2004 - 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** @file user_connect.h + + @brief + Declaration of the user_connect class. + + @note + + @see + /sql/handler.h and /storage/connect/user_connect.cc +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#if defined(WIN32) +#include <sys\timeb.h> +#else +#include <sys/timeb.h> +#endif // UBUNTU + +/*****************************************************************************/ +/* This is the global structure having all CONNECT information. */ +/*****************************************************************************/ +//typedef struct _global *PGLOBAL; +typedef class user_connect *PCONNECT; +typedef class ha_connect *PHC; +static int connect_done_func(void *); + +/*****************************************************************************/ +/* The CONNECT users. There should be one by connected users. */ +/*****************************************************************************/ +class user_connect +{ + friend class ha_connect; + friend int connect_done_func(void *); +public: + // Constructor + user_connect(THD *thd, const char *dbn); + + // Destructor + virtual ~user_connect(); + + // Implementation + bool user_init(ha_connect *hc); + bool CheckCleanup(void); + bool CheckQueryID(void) {return thdp->query_id > last_query_id;} + bool CheckQuery(query_id_t vid) {return last_query_id > vid;} + +protected: + // Members + static PCONNECT to_users; // To the chain of users + THD *thdp; // To the user thread + PCONNECT next; // Next user in chain + PCONNECT previous; // Previous user in chain + PGLOBAL g; // The common handle to CONNECT +//char dbname[32]; // The DBCONNECT database + query_id_t last_query_id; // the latest user query id + int count; // if used by several handlers + // Statistics + ulong nrd, fnd, nfd; + ulonglong tb1; +}; // end of user_connect class definition + diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp new file mode 100644 index 00000000000..4fbee8ba3df --- /dev/null +++ b/storage/connect/valblk.cpp @@ -0,0 +1,936 @@ +/************ Valblk C++ Functions Source Code File (.CPP) *************/ +/* Name: VALBLK.CPP Version 1.7 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* This file contains the VALBLK and derived classes functions. */ +/* Second family is VALBLK, representing simple suballocated arrays */ +/* of values treated sequentially by FIX, BIN and VCT tables and */ +/* columns, as well for min/max blocks as for VCT column blocks. */ +/* Q&A: why not using only one family ? Simple values are arrays that */ +/* have only one element and arrays could have functions for all kind */ +/* of processing. The answer is a-because historically it was simpler */ +/* to do that way, b-because of performance on single values, and c- */ +/* to avoid too complicated classes and unuseful duplication of many */ +/* functions used on one family only. The drawback is that for new */ +/* types of objects, we shall have more classes to update. */ +/* This is why we are now using a template class for many types. */ +/* Currently the only implemented types are PSZ, chars, int, short, */ +/* DATE, longlong, and double. Shortly we should add more types. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//#include <windows.h> +#else +#include "osutil.h" +#include "string.h" +#endif + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/* valblk.h is header containing VALBLK derived classes declares. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "valblk.h" + +#define CheckBlanks assert(!Blanks); + +/***********************************************************************/ +/* AllocValBlock: allocate a VALBLK according to type. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL g, void *mp, int type, int nval, int len, + int prec, bool check, bool blank) + { + PVBLK blkp; + +#ifdef DEBTRACE + htrc("AVB: mp=%p type=%d nval=%d len=%d check=%u blank=%u\n", + mp, type, nval, len, check, blank); +#endif + + switch (type) { + case TYPE_STRING: + if (len) + blkp = new(g) CHRBLK(mp, nval, len, prec, blank); + else + blkp = new(g) STRBLK(g, mp, nval); + + break; + case TYPE_SHORT: + blkp = new(g) TYPBLK<short>(mp, nval, type); + break; + case TYPE_INT: + blkp = new(g) TYPBLK<int>(mp, nval, type); + break; + case TYPE_DATE: // ????? + blkp = new(g) DATBLK(mp, nval); + break; + case TYPE_BIGINT: + blkp = new(g) TYPBLK<longlong>(mp, nval, type); + break; + case TYPE_FLOAT: + blkp = new(g) TYPBLK<double>(mp, nval, prec, type); + break; + case TYPE_TINY: + blkp = new(g) TYPBLK<char>(mp, nval, type); + break; + default: + sprintf(g->Message, MSG(BAD_VALBLK_TYPE), type); + return NULL; + } // endswitch Type + + blkp->Init(g, check); + return blkp; + } // end of AllocValBlock + +/* -------------------------- Class VALBLK --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +VALBLK::VALBLK(void *mp, int type, int nval) + { + Blkp = mp; + To_Nulls = NULL; + Check = true; + Nullable = false; + Type = type; + Nval = nval; + Prec = 0; + } // end of VALBLK constructor + +/***********************************************************************/ +/* Raise error for numeric types. */ +/***********************************************************************/ +PSZ VALBLK::GetCharValue(int n) + { + PGLOBAL& g = Global; + + assert(g); + sprintf(g->Message, MSG(NO_CHAR_FROM), Type); + longjmp(g->jumper[g->jump_level], Type); + return NULL; + } // end of GetCharValue + +/***********************************************************************/ +/* Set format so formatted dates can be converted on input. */ +/***********************************************************************/ +bool VALBLK::SetFormat(PGLOBAL g, PSZ fmt, int len, int year) + { + sprintf(g->Message, MSG(NO_DATE_FMT), Type); + return true; + } // end of SetFormat + +/***********************************************************************/ +/* Set the index of the location of value and return true if found. */ +/* To be used on ascending sorted arrays only. */ +/* Currently used by some BLKFIL classes only. */ +/***********************************************************************/ +bool VALBLK::Locate(PVAL vp, int& i) + { + ChkTyp(vp); + + int n = 1; + + for (i = 0; i < Nval; i++) + if ((n = CompVal(vp, i)) <= 0) + break; + + return (!n); + } // end of Locate + +/***********************************************************************/ +/* Set Nullable and allocate the Null array. */ +/***********************************************************************/ +void VALBLK::SetNullable(bool b) + { + if ((Nullable = b)) { + To_Nulls = (char*)PlugSubAlloc(Global, NULL, Nval); + memset(To_Nulls, 0, Nval); + } else + To_Nulls = NULL; + + } // end of SetNullable + +/***********************************************************************/ +/* Check functions. */ +/***********************************************************************/ +void VALBLK::ChkIndx(int n) + { + if (n < 0 || n >= Nval) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(BAD_VALBLK_INDX)); + longjmp(g->jumper[g->jump_level], Type); + } // endif n + + } // end of ChkIndx + +void VALBLK::ChkTyp(PVAL v) + { + if (Check && Type != v->GetType()) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(VALTYPE_NOMATCH)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Type + + } // end of ChkTyp + +void VALBLK::ChkTyp(PVBLK vb) + { + if (Check && Type != vb->GetType()) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(VALTYPE_NOMATCH)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Type + + } // end of ChkTyp + +/* -------------------------- Class TYPBLK --------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +template <class TYPE> +TYPBLK<TYPE>::TYPBLK(void *mp, int nval, int type) + : VALBLK(mp, type, nval), Typp((TYPE*&)Blkp) + { + Fmt = GetFmt(Type); + } // end of TYPBLK constructor + +template <class TYPE> +TYPBLK<TYPE>::TYPBLK(void *mp, int nval, int prec, int type) + : VALBLK(mp, type, nval), Typp((TYPE*&)Blkp) + { + DBUG_ASSERT(Type == TYPE_FLOAT); + Prec = prec; + Fmt = GetFmt(Type); + } // end of DBLBLK constructor + +/***********************************************************************/ +/* Initialization routine. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::Init(PGLOBAL g, bool check) + { + if (!Blkp) + Blkp = PlugSubAlloc(g, NULL, Nval * sizeof(TYPE)); + + Check = check; + Global = g; + } // end of Init + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::SetValue(PVAL valp, int n) + { + bool b; + + ChkIndx(n); + ChkTyp(valp); + + if (!(b = valp->IsNull() && Nullable)) + Typp[n] = GetTypedValue(valp); + else + Reset(n); + + SetNull(n, b); + } // end of SetValue + +template <> +int TYPBLK<int>::GetTypedValue(PVAL valp) + {return valp->GetIntValue();} + +template <> +short TYPBLK<short>::GetTypedValue(PVAL valp) + {return valp->GetShortValue();} + +template <> +longlong TYPBLK<longlong>::GetTypedValue(PVAL valp) + {return valp->GetBigintValue();} + +template <> +double TYPBLK<double>::GetTypedValue(PVAL valp) + {return valp->GetFloatValue();} + +template <> +char TYPBLK<char>::GetTypedValue(PVAL valp) + {return valp->GetTinyValue();} + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::SetValue(PSZ p, int n) + { + ChkIndx(n); + + if (Check) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(BAD_SET_STRING)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Check + + Typp[n] = GetTypedValue(p); + SetNull(n, false); + } // end of SetValue + +template <> +int TYPBLK<int>::GetTypedValue(PSZ p) {return atol(p);} +template <> +short TYPBLK<short>::GetTypedValue(PSZ p) {return (short)atoi(p);} +template <> +longlong TYPBLK<longlong>::GetTypedValue(PSZ p) {return atoll(p);} +template <> +double TYPBLK<double>::GetTypedValue(PSZ p) {return atof(p);} +template <> +char TYPBLK<char>::GetTypedValue(PSZ p) {return (char)atoi(p);} + +/***********************************************************************/ +/* Set one value in a block from a value in another block. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::SetValue(PVBLK pv, int n1, int n2) + { + bool b; + + ChkIndx(n1); + ChkTyp(pv); + + if (!(b = pv->IsNull(n2) && Nullable)) + Typp[n1] = GetTypedValue(pv, n2); + else + Reset(n1); + + SetNull(n1, b); + } // end of SetValue + +template <> +int TYPBLK<int>::GetTypedValue(PVBLK blk, int n) + {return blk->GetIntValue(n);} + +template <> +short TYPBLK<short>::GetTypedValue(PVBLK blk, int n) + {return blk->GetShortValue(n);} + +template <> +longlong TYPBLK<longlong>::GetTypedValue(PVBLK blk, int n) + {return blk->GetBigintValue(n);} + +template <> +double TYPBLK<double>::GetTypedValue(PVBLK blk, int n) + {return blk->GetFloatValue(n);} + +template <> +char TYPBLK<char>::GetTypedValue(PVBLK blk, int n) + {return blk->GetTinyValue(n);} + +#if 0 +/***********************************************************************/ +/* Set many values in a block from values in another block. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::SetValues(PVBLK pv, int k, int n) + { + CheckType(pv) + TYPE *lp = ((TYPBLK*)pv)->Typp; + + for (register int i = k; i < n; i++) // TODO + Typp[i] = lp[i]; + + } // end of SetValues +#endif // 0 + +/***********************************************************************/ +/* Move one value from i to j. */ +/***********************************************************************/ +template <class TYPE> +void TYPBLK<TYPE>::Move(int i, int j) + { + Typp[j] = Typp[i]; + MoveNull(i, j); + } // end of Move + +/***********************************************************************/ +/* Compare a Value object with the nth value of the block. */ +/***********************************************************************/ +template <class TYPE> +int TYPBLK<TYPE>::CompVal(PVAL vp, int n) + { +#if defined(_DEBUG) + ChkIndx(n); + ChkTyp(vp); +#endif // _DEBUG + TYPE mlv = Typp[n]; + TYPE vlv = GetTypedValue(vp); + + return (vlv > mlv) ? 1 : (vlv < mlv) ? (-1) : 0; + } // end of CompVal + +/***********************************************************************/ +/* Compare two values of the block. */ +/***********************************************************************/ +template <class TYPE> +int TYPBLK<TYPE>::CompVal(int i1, int i2) + { + TYPE lv1 = Typp[i1]; + TYPE lv2 = Typp[i2]; + + return (lv1 > lv2) ? 1 : (lv1 < lv2) ? (-1) : 0; + } // end of CompVal + +/***********************************************************************/ +/* Get a pointer on the nth value of the block. */ +/***********************************************************************/ +template <class TYPE> +void *TYPBLK<TYPE>::GetValPtr(int n) + { + ChkIndx(n); + return Typp + n; + } // end of GetValPtr + +/***********************************************************************/ +/* Get a pointer on the nth value of the block. */ +/***********************************************************************/ +template <class TYPE> +void *TYPBLK<TYPE>::GetValPtrEx(int n) + { + ChkIndx(n); + return Typp + n; + } // end of GetValPtrEx + +/***********************************************************************/ +/* Returns index of matching value in block or -1. */ +/***********************************************************************/ +template <class TYPE> +int TYPBLK<TYPE>::Find(PVAL vp) + { + ChkTyp(vp); + + int i; + TYPE n = GetTypedValue(vp); + + for (i = 0; i < Nval; i++) + if (n == Typp[i]) + break; + + return (i < Nval) ? i : (-1); + } // end of Find + +/***********************************************************************/ +/* Returns the length of the longest string in the block. */ +/***********************************************************************/ +template <class TYPE> +int TYPBLK<TYPE>::GetMaxLength(void) + { + char buf[12]; + int i, n; + + for (i = n = 0; i < Nval; i++) { + sprintf(buf, Fmt, Typp[i]); + + n = max(n, (signed)strlen(buf)); + } // endfor i + + return n; + } // end of GetMaxLength + + +/* -------------------------- Class CHRBLK --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +CHRBLK::CHRBLK(void *mp, int nval, int len, int prec, bool blank) + : VALBLK(mp, TYPE_STRING, nval), Chrp((char*&)Blkp) + { + Valp = NULL; + Blanks = blank; + Ci = (prec != 0); + Long = len; + } // end of CHRBLK constructor + +/***********************************************************************/ +/* Initialization routine. */ +/***********************************************************************/ +void CHRBLK::Init(PGLOBAL g, bool check) + { + Valp = (char*)PlugSubAlloc(g, NULL, Long + 1); + Valp[Long] = '\0'; + + if (!Blkp) + Blkp = PlugSubAlloc(g, NULL, Nval * Long); + + Check = check; + Global = g; + } // end of Init + +/***********************************************************************/ +/* Reset nth element to a null string. */ +/***********************************************************************/ +void CHRBLK::Reset(int n) + { + if (Blanks) + memset(Chrp + n * Long, ' ', Long); + else + *(Chrp + n * Long) = '\0'; + + } // end of Reset + +/***********************************************************************/ +/* Return the zero ending value of the nth element. */ +/***********************************************************************/ +char *CHRBLK::GetCharValue(int n) + { + return (char *)GetValPtrEx(n); + } // end of GetCharValue + +/***********************************************************************/ +/* Return the value of the nth element converted to short. */ +/***********************************************************************/ +short CHRBLK::GetShortValue(int n) + { + return (short)atoi((char *)GetValPtrEx(n)); + } // end of GetShortValue + +/***********************************************************************/ +/* Return the value of the nth element converted to int. */ +/***********************************************************************/ +int CHRBLK::GetIntValue(int n) + { + return atol((char *)GetValPtrEx(n)); + } // end of GetIntValue + +/***********************************************************************/ +/* Return the value of the nth element converted to big int. */ +/***********************************************************************/ +longlong CHRBLK::GetBigintValue(int n) + { + return atoll((char *)GetValPtrEx(n)); + } // end of GetBigintValue + +/***********************************************************************/ +/* Return the value of the nth element converted to double. */ +/***********************************************************************/ +double CHRBLK::GetFloatValue(int n) + { + return atof((char *)GetValPtrEx(n)); + } // end of GetFloatValue + +/***********************************************************************/ +/* Return the value of the nth element converted to tiny int. */ +/***********************************************************************/ +char CHRBLK::GetTinyValue(int n) + { + return (char)atoi((char *)GetValPtrEx(n)); + } // end of GetTinyValue + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +void CHRBLK::SetValue(PVAL valp, int n) + { + bool b; + + ChkIndx(n); + ChkTyp(valp); + + if (!(b = valp->IsNull() && Nullable)) + SetValue((PSZ)valp->GetCharValue(), n); + else + Reset(n); + + SetNull(n, b); + } // end of SetValue + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +void CHRBLK::SetValue(PSZ sp, int n) + { + size_t len = (sp) ? strlen(sp) : 0; + char *p = Chrp + n * Long; + +#if defined(_DEBUG) || defined(DEBTRACE) + if (Check && (signed)len > Long) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(SET_STR_TRUNC)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Check +#endif + + if (sp) + strncpy(p, sp, Long); + else + *p = '\0'; + + if (Blanks) + // Suppress eventual ending zero and right fill with blanks + for (register int i = len; i < Long; i++) + p[i] = ' '; + + SetNull(n, false); + } // end of SetValue + +/***********************************************************************/ +/* Set one value in a block from a value in another block. */ +/***********************************************************************/ +void CHRBLK::SetValue(PVBLK pv, int n1, int n2) + { + bool b; + + if (Type != pv->GetType() || Long != ((CHRBLK*)pv)->Long) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(BLKTYPLEN_MISM)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Type + + if (!(b = pv->IsNull(n2) && Nullable)) + memcpy(Chrp + n1 * Long, ((CHRBLK*)pv)->Chrp + n2 * Long, Long); + else + Reset(n1); + + SetNull(n1, b); + } // end of SetValue + +#if 0 +/***********************************************************************/ +/* Set many values in a block from values in another block. */ +/***********************************************************************/ +void CHRBLK::SetValues(PVBLK pv, int k, int n) + { +#if defined(_DEBUG) || defined(DEBTRACE) + if (Type != pv->GetType() || Long != ((CHRBLK*)pv)->Long) { + PGLOBAL& g = Global; + strcpy(g->Message, MSG(BLKTYPLEN_MISM)); + longjmp(g->jumper[g->jump_level], Type); + } // endif Type +#endif + char *p = ((CHRBLK*)pv)->Chrp; + + if (!k) + memcpy(Chrp, p, Long * n); + else + memcpy(Chrp + k * Long, p + k * Long, Long * (n - k)); + + } // end of SetValues +#endif // 0 + +/***********************************************************************/ +/* Move one value from i to j. */ +/***********************************************************************/ +void CHRBLK::Move(int i, int j) + { + memcpy(Chrp + j * Long, Chrp + i * Long, Long); + MoveNull(i, j); + } // end of Move + +/***********************************************************************/ +/* Compare a Value object with the nth value of the block. */ +/***********************************************************************/ +int CHRBLK::CompVal(PVAL vp, int n) + { + ChkIndx(n); + ChkTyp(vp); + + char *xvp = vp->GetCharValue(); // Get Value zero ended string + bool ci = Ci || vp->IsCi(); // true if is case insensitive + + GetValPtrEx(n); // Get a zero ended string in Valp + return (ci) ? stricmp(xvp, Valp) : strcmp(xvp, Valp); + } // end of CompVal + +/***********************************************************************/ +/* Compare two values of the block. */ +/***********************************************************************/ +int CHRBLK::CompVal(int i1, int i2) + { + return (Ci) ? strnicmp(Chrp + i1 * Long, Chrp + i2 * Long, Long) + : strncmp(Chrp + i1 * Long, Chrp + i2 * Long, Long); + } // end of CompVal + +/***********************************************************************/ +/* Get a pointer on the nth value of the block. */ +/***********************************************************************/ +void *CHRBLK::GetValPtr(int n) + { + ChkIndx(n); + return Chrp + n * Long; + } // end of GetValPtr + +/***********************************************************************/ +/* Get a pointer on a zero ended string equal to nth value. */ +/***********************************************************************/ +void *CHRBLK::GetValPtrEx(int n) + { + ChkIndx(n); + memcpy(Valp, Chrp + n * Long, Long); + + if (IsNull(n)) + return ""; + + if (Blanks) { + // The (fast) way this is done works only for blocks such + // as Min and Max where strings are stored with the ending 0 + // except for those whose length is equal to Len. + // For VCT blocks we must remove rightmost blanks. + char *p = Valp + Long; + + for (p--; *p == ' ' && p >= Valp; p--) ; + + *(++p) = '\0'; + } // endif Blanks + + return Valp; + } // end of GetValPtrEx + +/***********************************************************************/ +/* Returns index of matching value in block or -1. */ +/***********************************************************************/ +int CHRBLK::Find(PVAL vp) + { + ChkTyp(vp); + + int i; + bool ci = Ci || vp->IsCi(); + PSZ s = vp->GetCharValue(); + + if (vp->IsNull()) + return -1; + + for (i = 0; i < Nval; i++) { + if (IsNull(i)) + continue; + + GetValPtrEx(i); // Get a zero ended string in Valp + + if (!((ci) ? strnicmp(s, Valp, Long) : strncmp(s, Valp, Long))) + break; + + } // endfor i + + return (i < Nval) ? i : (-1); + } // end of Find + +/***********************************************************************/ +/* Returns the length of the longest string in the block. */ +/***********************************************************************/ +int CHRBLK::GetMaxLength(void) + { + int i, n; + + for (i = n = 0; i < Nval; i++) + if (!IsNull(i)) { + GetValPtrEx(i); + n = max(n, (signed)strlen(Valp)); + } // endif null + + return n; + } // end of GetMaxLength + + +/* -------------------------- Class STRBLK --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +STRBLK::STRBLK(PGLOBAL g, void *mp, int nval) + : VALBLK(mp, TYPE_STRING, nval), Strp((PSZ*&)Blkp) + { + Global = g; + Nullable = true; + } // end of STRBLK constructor + +/***********************************************************************/ +/* Initialization routine. */ +/***********************************************************************/ +void STRBLK::Init(PGLOBAL g, bool check) + { + if (!Blkp) + Blkp = PlugSubAlloc(g, NULL, Nval * sizeof(PSZ)); + + Check = check; + Global = g; + } // end of Init + +/***********************************************************************/ +/* Set one value in a block from a value in another block. */ +/***********************************************************************/ +void STRBLK::SetValue(PVBLK pv, int n1, int n2) + { + ChkTyp(pv); + Strp[n1] = (!pv->IsNull(n2)) ? ((STRBLK*)pv)->Strp[n2] : NULL; + } // end of SetValue + +#if 0 +/***********************************************************************/ +/* Set many values in a block from values in another block. */ +/***********************************************************************/ +void STRBLK::SetValues(PVBLK pv, int k, int n) + { + CheckType(pv) + PSZ *sp = ((STRBLK*)pv)->Strp; + + for (register int i = k; i < n; i++) + Strp[i] = (!pv->IsNull(i)) ? sp[i] : NULL; + + } // end of SetValues +#endif // 0 + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +void STRBLK::SetValue(PVAL valp, int n) + { + ChkIndx(n); + ChkTyp(valp); + + if (!valp->IsNull()) + SetValue((PSZ)valp->GetCharValue(), n); + else + Strp[n] = NULL; + + } // end of SetValue + +/***********************************************************************/ +/* Set one value in a block. */ +/***********************************************************************/ +void STRBLK::SetValue(PSZ p, int n) + { + Strp[n] = (PSZ)PlugSubAlloc(Global, NULL, strlen(p) + 1); + strcpy(Strp[n], p); + } // end of SetValue + +/***********************************************************************/ +/* Move one value from i to j. */ +/***********************************************************************/ +void STRBLK::Move(int i, int j) + { + Strp[j] = Strp[i]; + } // end of Move + +/***********************************************************************/ +/* Compare a Value object with the nth value of the block. */ +/***********************************************************************/ +int STRBLK::CompVal(PVAL vp, int n) + { + ChkIndx(n); + ChkTyp(vp); + + if (vp->IsNull() || !Strp[n]) + DBUG_ASSERT(false); + + return strcmp(vp->GetCharValue(), Strp[n]); + } // end of CompVal + +/***********************************************************************/ +/* Compare two values of the block. */ +/***********************************************************************/ +int STRBLK::CompVal(int i1, int i2) + { + if (!Strp[i1] || !Strp[i2]) + DBUG_ASSERT(false); + + return (strcmp(Strp[i1], Strp[i2])); + } // end of CompVal + +/***********************************************************************/ +/* Get a pointer on the nth value of the block. */ +/***********************************************************************/ +void *STRBLK::GetValPtr(int n) + { + ChkIndx(n); + return Strp + n; + } // end of GetValPtr + +/***********************************************************************/ +/* Get a pointer on a zero ended string equal to nth value. */ +/***********************************************************************/ +void *STRBLK::GetValPtrEx(int n) + { + ChkIndx(n); + return (Strp[n]) ? Strp[n] : ""; + } // end of GetValPtrEx + +/***********************************************************************/ +/* Returns index of matching value in block or -1. */ +/***********************************************************************/ +int STRBLK::Find(PVAL vp) + { + int i; + PSZ s; + + ChkTyp(vp); + + if (vp->IsNull()) + return -1; + else + s = vp->GetCharValue(); + + for (i = 0; i < Nval; i++) + if (Strp[i] && !strcmp(s, Strp[i])) + break; + + return (i < Nval) ? i : (-1); + } // end of Find + +/***********************************************************************/ +/* Returns the length of the longest string in the block. */ +/***********************************************************************/ +int STRBLK::GetMaxLength(void) + { + int i, n; + + for (i = n = 0; i < Nval; i++) + if (Strp[i]) + n = max(n, (signed)strlen(Strp[i])); + + return n; + } // end of GetMaxLength + +/* -------------------------- Class DATBLK --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +DATBLK::DATBLK(void *mp, int nval) : TYPBLK<int>(mp, nval, TYPE_INT) + { + Type = TYPE_DATE; + Dvalp = NULL; + } // end of DATBLK constructor + +/***********************************************************************/ +/* Set format so formatted dates can be converted on input. */ +/***********************************************************************/ +bool DATBLK::SetFormat(PGLOBAL g, PSZ fmt, int len, int year) + { + if (!(Dvalp = AllocateValue(g, TYPE_DATE, len, year, fmt))) + return true; + + return false; + } // end of SetFormat + +/***********************************************************************/ +/* Set one value in a block from a char string. */ +/***********************************************************************/ +void DATBLK::SetValue(PSZ p, int n) + { + if (Dvalp) { + // Decode the string according to format + Dvalp->SetValue_psz(p); + Typp[n] = Dvalp->GetIntValue(); + } else + TYPBLK<int>::SetValue(p, n); + + } // end of SetValue + +/* ------------------------- End of Valblk --------------------------- */ + diff --git a/storage/connect/valblk.h b/storage/connect/valblk.h new file mode 100644 index 00000000000..9a85577a104 --- /dev/null +++ b/storage/connect/valblk.h @@ -0,0 +1,260 @@ +/*************** Valblk H Declares Source Code File (.H) ***************/ +/* Name: VALBLK.H Version 1.9 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */ +/* */ +/* This file contains the VALBLK and derived classes declares. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include required application header files */ +/* assert.h is header required when using the assert function. */ +/* block.h is header containing Block global declarations. */ +/***********************************************************************/ +#ifndef __VALBLK__H__ +#define __VALBLK__H__ +#include "value.h" + +/***********************************************************************/ +/* Utility used to allocate value blocks. */ +/***********************************************************************/ +DllExport PVBLK AllocValBlock(PGLOBAL, void*, int, int, int, int, bool, bool); +const char *GetFmt(int type); + +/***********************************************************************/ +/* Class VALBLK represent a base class for variable blocks. */ +/***********************************************************************/ +class VALBLK : public BLOCK { +//friend void SemColData(PGLOBAL g, PSEM semp); + public: + // Constructors + VALBLK(void *mp, int type, int nval); + + // Implementation + int GetNval(void) {return Nval;} + void SetNval(int n) {Nval = n;} + void *GetValPointer(void) {return Blkp;} + void SetValPointer(void *mp) {Blkp = mp;} + int GetType(void) {return Type;} + void SetCheck(bool b) {Check = b;} + void MoveNull(int i, int j) + {if (To_Nulls) To_Nulls[j] = To_Nulls[j];} + virtual void SetNull(int n, bool b) + {if (To_Nulls) {To_Nulls[n] = (b) ? '*' : 0;}} + virtual bool IsNull(int n) {return To_Nulls && To_Nulls[n];} + virtual void SetNullable(bool b); + virtual void Init(PGLOBAL g, bool check) = 0; + virtual int GetVlen(void) = 0; + virtual PSZ GetCharValue(int n); + virtual short GetShortValue(int n) = 0; + virtual int GetIntValue(int n) = 0; + virtual longlong GetBigintValue(int n) = 0; + virtual double GetFloatValue(int n) = 0; + virtual char GetTinyValue(int n) = 0; + virtual void ReAlloc(void *mp, int n) {Blkp = mp; Nval = n;} + virtual void Reset(int n) = 0; + virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0); + virtual void SetPrec(int p) {} + virtual bool IsCi(void) {return false;} + + // Methods + virtual void SetValue(short sval, int n) {assert(false);} + virtual void SetValue(int lval, int n) {assert(false);} + virtual void SetValue(longlong lval, int n) {assert(false);} + virtual void SetValue(double fval, int n) {assert(false);} + virtual void SetValue(char cval, int n) {assert(false);} + virtual void SetValue(PSZ sp, int n) {assert(false);} + virtual void SetValue(PVAL valp, int n) = 0; + virtual void SetValue(PVBLK pv, int n1, int n2) = 0; +#if 0 + virtual void SetMin(PVAL valp, int n) = 0; + virtual void SetMax(PVAL valp, int n) = 0; + virtual void SetValues(PVBLK pv, int i, int n) = 0; + virtual void AddMinus1(PVBLK pv, int n1, int n2) {assert(false);} +#endif // 0 + virtual void Move(int i, int j) = 0; + virtual int CompVal(PVAL vp, int n) = 0; + virtual int CompVal(int i1, int i2) = 0; + virtual void *GetValPtr(int n) = 0; + virtual void *GetValPtrEx(int n) = 0; + virtual int Find(PVAL vp) = 0; + virtual int GetMaxLength(void) = 0; + bool Locate(PVAL vp, int& i); + + protected: + void ChkIndx(int n); + void ChkTyp(PVAL v); + void ChkTyp(PVBLK vb); + + // Members + PGLOBAL Global; // Used for messages and allocation + char *To_Nulls; // Null values array + void *Blkp; // To value block + bool Check; // If true SetValue types must match + bool Nullable; // True if values can be null + int Type; // Type of individual values + int Nval; // Max number of values in block + int Prec; // Precision of float values + }; // end of class VALBLK + +/***********************************************************************/ +/* Class TYPBLK: represents a block of typed values. */ +/***********************************************************************/ +template <class TYPE> +class TYPBLK : public VALBLK { + public: + // Constructors + TYPBLK(void *mp, int size, int type); + TYPBLK(void *mp, int size, int prec, int type); + + // Implementation + virtual void Init(PGLOBAL g, bool check); + virtual int GetVlen(void) {return sizeof(int);} +//virtual PSZ GetCharValue(int n); + virtual short GetShortValue(int n) {return (short)Typp[n];} + virtual int GetIntValue(int n) {return (int)Typp[n];} + virtual longlong GetBigintValue(int n) {return (longlong)Typp[n];} + virtual double GetFloatValue(int n) {return (double)Typp[n];} + virtual char GetTinyValue(int n) {return (char)Typp[n];} + virtual void Reset(int n) {Typp[n] = 0;} + + // Methods + virtual void SetValue(PSZ sp, int n); + virtual void SetValue(short sval, int n) + {Typp[n] = (TYPE)sval; SetNull(n, false);} + virtual void SetValue(int lval, int n) + {Typp[n] = (TYPE)lval; SetNull(n, false);} + virtual void SetValue(longlong lval, int n) + {Typp[n] = (TYPE)lval; SetNull(n, false);} + virtual void SetValue(double fval, int n) + {Typp[n] = (TYPE)fval; SetNull(n, false);} + virtual void SetValue(char cval, int n) + {Typp[n] = (TYPE)cval; SetNull(n, false);} + virtual void SetValue(PVAL valp, int n); + virtual void SetValue(PVBLK pv, int n1, int n2); +//virtual void SetValues(PVBLK pv, int k, int n); + virtual void Move(int i, int j); + virtual int CompVal(PVAL vp, int n); + virtual int CompVal(int i1, int i2); + virtual void *GetValPtr(int n); + virtual void *GetValPtrEx(int n); + virtual int Find(PVAL vp); + virtual int GetMaxLength(void); + + protected: + // Specialized functions + TYPE GetTypedValue(PVAL vp); + TYPE GetTypedValue(PVBLK blk, int n); + TYPE GetTypedValue(PSZ s); + + // Members + TYPE* const &Typp; + const char *Fmt; + }; // end of class TYPBLK + +/***********************************************************************/ +/* Class CHRBLK: represent a block of fixed length strings. */ +/***********************************************************************/ +class CHRBLK : public VALBLK { + public: + // Constructors + CHRBLK(void *mp, int size, int len, int prec, bool b); + + // Implementation + virtual void Init(PGLOBAL g, bool check); + virtual int GetVlen(void) {return Long;} + virtual PSZ GetCharValue(int n); + virtual short GetShortValue(int n); + virtual int GetIntValue(int n); + virtual longlong GetBigintValue(int n); + virtual double GetFloatValue(int n); + virtual char GetTinyValue(int n); + virtual void Reset(int n); + virtual void SetPrec(int p) {Ci = (p != 0);} + virtual bool IsCi(void) {return Ci;} + + // Methods + virtual void SetValue(PSZ sp, int n); + virtual void SetValue(PVAL valp, int n); + virtual void SetValue(PVBLK pv, int n1, int n2); +//virtual void SetValues(PVBLK pv, int k, int n); + virtual void Move(int i, int j); + virtual int CompVal(PVAL vp, int n); + virtual int CompVal(int i1, int i2); + virtual void *GetValPtr(int n); + virtual void *GetValPtrEx(int n); + virtual int Find(PVAL vp); + virtual int GetMaxLength(void); + + protected: + // Members + char* const &Chrp; // Pointer to char buffer + PSZ Valp; // Used to make a zero ended value + bool Blanks; // True for right filling with blanks + bool Ci; // True if case insensitive + int Long; // Length of each string + }; // end of class CHRBLK + +/***********************************************************************/ +/* Class STRBLK: represent a block of string pointers. */ +/* Currently this class is used only by the DECODE scalar function */ +/* and by the MyColumn function to store date formats. */ +/***********************************************************************/ +class STRBLK : public VALBLK { + public: + // Constructors + STRBLK(PGLOBAL g, void *mp, int size); + + // Implementation + virtual void SetNull(int n, bool b) {if (b) {Strp[n] = NULL;}} + virtual bool IsNull(int n) {return Strp[n] == NULL;} + virtual void SetNullable(bool b) {} // Always nullable + virtual void Init(PGLOBAL g, bool check); + virtual int GetVlen(void) {return sizeof(PSZ);} + virtual PSZ GetCharValue(int n) {return Strp[n];} + virtual short GetShortValue(int n) {return (short)atoi(Strp[n]);} + virtual int GetIntValue(int n) {return atol(Strp[n]);} + virtual longlong GetBigintValue(int n) {return atoll(Strp[n]);} + virtual double GetFloatValue(int n) {return atof(Strp[n]);} + virtual char GetTinyValue(int n) {return (char)atoi(Strp[n]);} + virtual void Reset(int n) {Strp[n] = NULL;} + + // Methods + virtual void SetValue(PSZ sp, int n); + virtual void SetValue(PVAL valp, int n); + virtual void SetValue(PVBLK pv, int n1, int n2); +//virtual void SetValues(PVBLK pv, int k, int n); + virtual void Move(int i, int j); + virtual int CompVal(PVAL vp, int n); + virtual int CompVal(int i1, int i2); + virtual void *GetValPtr(int n); + virtual void *GetValPtrEx(int n); + virtual int Find(PVAL vp); + virtual int GetMaxLength(void); + + protected: + // Members + PSZ* const &Strp; // Pointer to PSZ buffer + }; // end of class STRBLK + +/***********************************************************************/ +/* Class DATBLK: represents a block of time stamp values. */ +/***********************************************************************/ +class DATBLK : public TYPBLK<int> { + public: + // Constructor + DATBLK(void *mp, int size); + + // Implementation + virtual bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0); + + // Methods + virtual void SetValue(PSZ sp, int n); + + protected: + // Members + PVAL Dvalp; // Date value used to convert string + }; // end of class DATBLK + +#endif // __VALBLK__H__ + diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp new file mode 100644 index 00000000000..c88c22dc3db --- /dev/null +++ b/storage/connect/value.cpp @@ -0,0 +1,1684 @@ +/************* Value C++ Functions Source Code File (.CPP) *************/ +/* Name: VALUE.CPP Version 2.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */ +/* */ +/* This file contains the VALUE and derived classes family functions. */ +/* These classes contain values of different types. They are used so */ +/* new object types can be defined and added to the processing simply */ +/* (hopefully) adding their specific functions in this file. */ +/* First family is VALUE that represent single typed objects. It is */ +/* used by columns (COLBLK), SELECT and FILTER (derived) objects. */ +/* Second family is VALBLK, representing simple suballocated arrays */ +/* of values treated sequentially by FIX, BIN and VCT tables and */ +/* columns, as well for min/max blocks as for VCT column blocks. */ +/* Q&A: why not using only one family ? Simple values are arrays that */ +/* have only one element and arrays could have functions for all kind */ +/* of processing. The answer is a-because historically it was simpler */ +/* to do that way, b-because of performance on single values, and c- */ +/* to avoid too complicated classes and unuseful duplication of many */ +/* functions used on one family only. The drawback is that for new */ +/* types of objects, we shall have more classes to update. */ +/* Currently the only implemented types are STRING, INT, SHORT, TINY, */ +/* DATE and LONGLONG. Shortly we should add at least UNSIGNED types. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +//#include <windows.h> +#else // !WIN32 +#include <string.h> +#endif // !WIN32 + +#include <math.h> + +#undef DOMAIN // Was defined in math.h + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "preparse.h" // For DATPAR +//#include "value.h" +#include "valblk.h" +#define NO_FUNC // Already defined in ODBConn +#include "plgcnx.h" // For DB types + +/***********************************************************************/ +/* Check macro's. */ +/***********************************************************************/ +#if defined(_DEBUG) +#define CheckType(V) if (Type != V->GetType()) { \ + PGLOBAL& g = Global; \ + strcpy(g->Message, MSG(VALTYPE_NOMATCH)); \ + longjmp(g->jumper[g->jump_level], Type); } +#else +#define CheckType(V) +#endif + +#define FOURYEARS 126230400 // Four years in seconds (1 leap) + +/***********************************************************************/ +/* Static variables. */ +/***********************************************************************/ +static char *list = + " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/.*-‘abcdefghijklmnopqrstuv"; //wxyzñ' +//" ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz."; +extern "C" int trace; + +/***********************************************************************/ +/* Initialize the DTVAL static member. */ +/***********************************************************************/ +int DTVAL::Shift = 0; + +/***********************************************************************/ +/* Routines called externally. */ +/***********************************************************************/ +bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool); +#if !defined(WIN32) +extern "C" { +PSZ strupr(PSZ s); +PSZ strlwr(PSZ s); +} +#endif // !WIN32 + +/***********************************************************************/ +/* Returns the bitmap representing the conditions that must not be */ +/* met when returning from TestValue for a given operator. */ +/* Bit one is EQ, bit 2 is LT, and bit 3 is GT. */ +/***********************************************************************/ +BYTE OpBmp(PGLOBAL g, OPVAL opc) + { + BYTE bt; + + switch (opc) { + case OP_IN: + case OP_EQ: bt = 0x06; break; + case OP_NE: bt = 0x01; break; + case OP_GT: bt = 0x03; break; + case OP_GE: bt = 0x02; break; + case OP_LT: bt = 0x05; break; + case OP_LE: bt = 0x04; break; + case OP_EXIST: bt = 0x00; break; + default: + sprintf(g->Message, MSG(BAD_FILTER_OP), opc); + longjmp(g->jumper[g->jump_level], 777); + } // endswitch opc + + return bt; + } // end of OpBmp + +/***********************************************************************/ +/* GetTypeName: returns the PlugDB internal type name. */ +/***********************************************************************/ +PSZ GetTypeName(int type) + { + PSZ name = "UNKNOWN"; + + switch (type) { + case TYPE_STRING: name = "CHAR"; break; + case TYPE_SHORT: name = "SMALLINT"; break; + case TYPE_INT: name = "INTEGER"; break; + case TYPE_BIGINT: name = "BIGINT"; break; + case TYPE_DATE: name = "DATE"; break; + case TYPE_FLOAT: name = "FLOAT"; break; + case TYPE_TINY: name = "TINY"; break; + } // endswitch type + + return name; + } // end of GetTypeName + +/***********************************************************************/ +/* GetTypeSize: returns the PlugDB internal type size. */ +/***********************************************************************/ +int GetTypeSize(int type, int len) + { + switch (type) { + case TYPE_STRING: len = len * sizeof(char); break; + case TYPE_SHORT: len = sizeof(short); break; + case TYPE_INT: len = sizeof(int); break; + case TYPE_BIGINT: len = sizeof(longlong); break; + case TYPE_DATE: len = sizeof(int); break; + case TYPE_FLOAT: len = sizeof(double); break; + case TYPE_TINY: len = sizeof(char); break; + break; + default: len = 0; + } // endswitch type + + return len; + } // end of GetTypeSize + +/***********************************************************************/ +/* GetPLGType: returns the PlugDB type corresponding to a DB type. */ +/***********************************************************************/ +int GetPLGType(int type) + { + int tp; + + switch (type) { + case DB_CHAR: + case DB_STRING: tp = TYPE_STRING; break; + case DB_SHORT: tp = TYPE_SHORT; break; + case DB_INT: tp = TYPE_INT; break; + case DB_DOUBLE: tp = TYPE_FLOAT; break; + case DB_DATE: tp = TYPE_DATE; break; + default: tp = TYPE_ERROR; + } // endswitch type + + return tp; + } // end of GetPLGType + +/***********************************************************************/ +/* GetDBType: returns the DB type corresponding to a PlugDB type. */ +/***********************************************************************/ +int GetDBType(int type) + { + int tp; + + switch (type) { + case TYPE_STRING: tp = DB_CHAR; break; + case TYPE_SHORT: tp = DB_SHORT; break; + case TYPE_INT: tp = DB_INT; break; + case TYPE_BIGINT: + case TYPE_FLOAT: tp = DB_DOUBLE; break; + case TYPE_DATE: tp = DB_DATE; break; + default: tp = DB_ERROR; + } // endswitch type + + return tp; + } // end of GetPLGType + +/***********************************************************************/ +/* GetFormatType: returns the FORMAT character(s) according to type. */ +/***********************************************************************/ +char *GetFormatType(int type) + { + char *c = "X"; + + switch (type) { + case TYPE_STRING: c = "C"; break; + case TYPE_SHORT: c = "S"; break; + case TYPE_INT: c = "N"; break; + case TYPE_BIGINT: c = "L"; break; + case TYPE_FLOAT: c = "F"; break; + case TYPE_DATE: c = "D"; break; + case TYPE_TINY: c = "T"; break; + } // endswitch type + + return c; + } // end of GetFormatType + +/***********************************************************************/ +/* GetFormatType: returns the FORMAT type according to character. */ +/***********************************************************************/ +int GetFormatType(char c) + { + int type = TYPE_ERROR; + + switch (c) { + case 'C': type = TYPE_STRING; break; + case 'S': type = TYPE_SHORT; break; + case 'N': type = TYPE_INT; break; + case 'L': type = TYPE_BIGINT; break; + case 'F': type = TYPE_FLOAT; break; + case 'D': type = TYPE_DATE; break; + case 'T': type = TYPE_TINY; break; + } // endswitch type + + return type; + } // end of GetFormatType + + +/***********************************************************************/ +/* IsTypeChar: returns true for character type(s). */ +/***********************************************************************/ +bool IsTypeChar(int type) + { + switch (type) { + case TYPE_STRING: + return true; + } // endswitch type + + return false; + } // end of IsTypeChar + +/***********************************************************************/ +/* IsTypeNum: returns true for numeric types. */ +/***********************************************************************/ +bool IsTypeNum(int type) + { + switch (type) { + case TYPE_INT: + case TYPE_BIGINT: + case TYPE_DATE: + case TYPE_FLOAT: + case TYPE_SHORT: + case TYPE_NUM: + case TYPE_TINY: + return true; + } // endswitch type + + return false; + } // end of IsTypeNum + +/***********************************************************************/ +/* GetFmt: returns the format to use with a typed value. */ +/***********************************************************************/ +const char *GetFmt(int type) + { + const char *fmt; + + switch (type) { + case TYPE_STRING: fmt = "%s"; break; + case TYPE_SHORT: fmt = "%hd"; break; + case TYPE_BIGINT: fmt = "%lld"; break; + case TYPE_FLOAT: fmt = "%.*lf"; break; + default: fmt = "%d"; break; + } // endswitch Type + + return fmt; + } // end of GetFmt + +/***********************************************************************/ +/* ConvertType: what this function does is to determine the type to */ +/* which should be converted a value so no precision would be lost. */ +/* This can be a numeric type if num is true or non numeric if false. */ +/* Note: this is an ultra simplified version of this function that */ +/* should become more and more complex as new types are added. */ +/* Not evaluated types (TYPE_VOID or TYPE_UNDEF) return false from */ +/* IsType... functions so match does not prevent correct setting. */ +/***********************************************************************/ +int ConvertType(int target, int type, CONV kind, bool match) + { + switch (kind) { + case CNV_CHAR: + if (match && (!IsTypeChar(target) || !IsTypeChar(type))) + return TYPE_ERROR; + + return TYPE_STRING; + case CNV_NUM: + if (match && (!IsTypeNum(target) || !IsTypeNum(type))) + return TYPE_ERROR; + + return (target == TYPE_FLOAT || type == TYPE_FLOAT) ? TYPE_FLOAT + : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE + : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT + : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT + : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT + : TYPE_TINY; + default: + if (target == TYPE_ERROR || target == type) + return type; + + if (match && ((IsTypeChar(target) && !IsTypeChar(type)) || + (IsTypeNum(target) && !IsTypeNum(type)))) + return TYPE_ERROR; + + return (target == TYPE_FLOAT || type == TYPE_FLOAT) ? TYPE_FLOAT + : (target == TYPE_DATE || type == TYPE_DATE) ? TYPE_DATE + : (target == TYPE_BIGINT || type == TYPE_BIGINT) ? TYPE_BIGINT + : (target == TYPE_INT || type == TYPE_INT) ? TYPE_INT + : (target == TYPE_SHORT || type == TYPE_SHORT) ? TYPE_SHORT + : (target == TYPE_STRING || type == TYPE_STRING) ? TYPE_STRING + : (target == TYPE_TINY || type == TYPE_TINY) ? TYPE_TINY + : TYPE_ERROR; + } // endswitch kind + + } // end of ConvertType + +/***********************************************************************/ +/* AllocateConstant: allocates a constant Value. */ +/***********************************************************************/ +PVAL AllocateValue(PGLOBAL g, void *value, short type) + { + PVAL valp; + + if (trace) + htrc("AllocateConstant: value=%p type=%hd\n", value, type); + + switch (type) { + case TYPE_STRING: + valp = new(g) TYPVAL<PSZ>((PSZ)value); + break; + case TYPE_SHORT: + valp = new(g) TYPVAL<short>(*(short*)value, TYPE_SHORT); + break; + case TYPE_INT: + valp = new(g) TYPVAL<int>(*(int*)value, TYPE_INT); + break; + case TYPE_BIGINT: + valp = new(g) TYPVAL<longlong>(*(longlong*)value, TYPE_BIGINT); + break; + case TYPE_FLOAT: + valp = new(g) TYPVAL<double>(*(double *)value, TYPE_FLOAT); + break; + case TYPE_TINY: + valp = new(g) TYPVAL<char>(*(char *)value, TYPE_TINY); + break; + default: + sprintf(g->Message, MSG(BAD_VALUE_TYPE), type); + return NULL; + } // endswitch Type + + valp->SetGlobal(g); + return valp; + } // end of AllocateValue + +/***********************************************************************/ +/* Allocate a variable Value according to type, length and precision. */ +/***********************************************************************/ +PVAL AllocateValue(PGLOBAL g, int type, int len, int prec, + PSZ dom, PCATLG cat) + { + PVAL valp; + + switch (type) { + case TYPE_STRING: + valp = new(g) TYPVAL<PSZ>(g, (PSZ)NULL, len, prec); + break; + case TYPE_DATE: + valp = new(g) DTVAL(g, len, prec, dom); + break; + case TYPE_INT: + valp = new(g) TYPVAL<int>((int)0, TYPE_INT); + break; + case TYPE_BIGINT: + valp = new(g) TYPVAL<longlong>((longlong)0, TYPE_BIGINT); + break; + case TYPE_SHORT: + valp = new(g) TYPVAL<short>((short)0, TYPE_SHORT); + break; + case TYPE_FLOAT: + valp = new(g) TYPVAL<double>(0.0, prec, TYPE_FLOAT); + break; + case TYPE_TINY: + valp = new(g) TYPVAL<char>((char)0, TYPE_TINY); + break; + default: + sprintf(g->Message, MSG(BAD_VALUE_TYPE), type); + return NULL; + } // endswitch type + + valp->SetGlobal(g); + return valp; + } // end of AllocateValue + +/***********************************************************************/ +/* Allocate a constant Value converted to newtype. */ +/* Can also be used to copy a Value eventually converted. */ +/***********************************************************************/ +PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype) + { + PSZ p, sp; + + if (newtype == TYPE_VOID) // Means allocate a value of the same type + newtype = valp->GetType(); + + switch (newtype) { + case TYPE_STRING: + p = (PSZ)PlugSubAlloc(g, NULL, 1 + valp->GetValLen()); + + if ((sp = valp->GetCharString(p)) != p) + strcpy (p, sp); + + valp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec()); + break; + case TYPE_SHORT: + valp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT); + break; + case TYPE_INT: + valp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT); + break; + case TYPE_BIGINT: + valp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT); + break; + case TYPE_DATE: + valp = new(g) DTVAL(g, valp->GetIntValue()); + break; + case TYPE_FLOAT: + valp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_FLOAT); + break; + case TYPE_TINY: + valp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY); + break; + default: + sprintf(g->Message, MSG(BAD_VALUE_TYPE), newtype); + return NULL; + } // endswitch type + + valp->SetGlobal(g); + return valp; + } // end of AllocateValue + + +/* -------------------------- Class VALUE ---------------------------- */ + +/***********************************************************************/ +/* Class VALUE protected constructor. */ +/***********************************************************************/ +VALUE::VALUE(int type) : Type(type) + { + Fmt = GetFmt(Type); + Xfmt = GetXfmt(); + Null = false; + Nullable = false; + Clen = 0; + Prec = 0; + } // end of VALUE constructor + +/***********************************************************************/ +/* VALUE GetXfmt: returns the extended format to use with typed value. */ +/***********************************************************************/ +const char *VALUE::GetXfmt(void) + { + const char *fmt; + + switch (Type) { + case TYPE_STRING: fmt = "%*s"; break; + case TYPE_SHORT: fmt = "%*hd"; break; + case TYPE_BIGINT: fmt = "%*lld"; break; + case TYPE_FLOAT: fmt = "%*.*lf"; break; + default: fmt = "%*d"; break; + } // endswitch Type + + return fmt; + } // end of GetFmt + +/* -------------------------- Class TYPVAL ---------------------------- */ + +/***********************************************************************/ +/* TYPVAL public constructor from a constant typed value. */ +/***********************************************************************/ +template <class TYPE> +TYPVAL<TYPE>::TYPVAL(TYPE n, int type) : VALUE(type) + { + Tval = n; + Clen = sizeof(TYPE); + Prec = (Type == TYPE_FLOAT) ? 2 : 0; + } // end of TYPVAL constructor + +/***********************************************************************/ +/* TYPVAL public constructor from typed value. */ +/***********************************************************************/ +template <class TYPE> +TYPVAL<TYPE>::TYPVAL(TYPE n, int prec, int type) : VALUE(type) + { + assert(Type == TYPE_FLOAT); + Tval = n; + Clen = sizeof(TYPE); + Prec = prec; + } // end of TYPVAL constructor + +/***********************************************************************/ +/* TYPVAL GetValLen: returns the print length of the typed object. */ +/***********************************************************************/ +template <class TYPE> +int TYPVAL<TYPE>::GetValLen(void) + { + char c[32]; + + return sprintf(c, Fmt, Tval); + } // end of GetValLen + +template <> +int TYPVAL<double>::GetValLen(void) + { + char c[32]; + + return sprintf(c, Fmt, Prec, Tval); + } // end of GetValLen + +/***********************************************************************/ +/* TYPVAL SetValue: copy the value of another Value object. */ +/* This function allows conversion if chktype is false. */ +/***********************************************************************/ +template <class TYPE> +bool TYPVAL<TYPE>::SetValue_pval(PVAL valp, bool chktype) + { + if (chktype && Type != valp->GetType()) + return true; + + if (!(Null = valp->IsNull() && Nullable)) + Tval = GetTypedValue(valp); + else + Reset(); + + return false; + } // end of SetValue + +template <> +short TYPVAL<short>::GetTypedValue(PVAL valp) + {return valp->GetShortValue();} + +template <> +int TYPVAL<int>::GetTypedValue(PVAL valp) + {return valp->GetIntValue();} + +template <> +longlong TYPVAL<longlong>::GetTypedValue(PVAL valp) + {return valp->GetBigintValue();} + +template <> +double TYPVAL<double>::GetTypedValue(PVAL valp) + {return valp->GetFloatValue();} + +template <> +char TYPVAL<char>::GetTypedValue(PVAL valp) + {return valp->GetTinyValue();} + +/***********************************************************************/ +/* TYPVAL SetValue: convert chars extracted from a line to TYPE value.*/ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::SetValue_char(char *p, int n) + { + char *p2, buf[32]; + bool minus; + + for (p2 = p + n; p < p2 && *p == ' '; p++) ; + + for (Tval = 0, minus = false; p < p2; p++) + switch (*p) { + case '-': + minus = true; + case '+': + break; + case '0': Tval = Tval * 10; break; + case '1': Tval = Tval * 10 + 1; break; + case '2': Tval = Tval * 10 + 2; break; + case '3': Tval = Tval * 10 + 3; break; + case '4': Tval = Tval * 10 + 4; break; + case '5': Tval = Tval * 10 + 5; break; + case '6': Tval = Tval * 10 + 6; break; + case '7': Tval = Tval * 10 + 7; break; + case '8': Tval = Tval * 10 + 8; break; + case '9': Tval = Tval * 10 + 9; break; + default: + p = p2; + } // endswitch *p + + if (minus && Tval) + Tval = - Tval; + + if (trace) + htrc(strcat(strcat(strcpy(buf, " setting %s to: "), Fmt), "\n"), + GetTypeName(Type), Tval); + + Null = false; + } // end of SetValue + +template <> +void TYPVAL<double>::SetValue_char(char *p, int n) + { + char *p2, buf[32]; + + for (p2 = p + n; p < p2 && *p == ' '; p++) ; + + n = min(p2 - p, 31); + memcpy(buf, p, n); + buf[n] = '\0'; + Tval = atof(buf); + + if (trace) + htrc(" setting double: '%s' -> %lf\n", buf, Tval); + + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* TYPVAL SetValue: fill a typed value from a string. */ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::SetValue_psz(PSZ s) + { + Tval = GetTypedValue(s); + Null = false; + } // end of SetValue + +template <> +int TYPVAL<int>::GetTypedValue(PSZ s) {return atol(s);} +template <> +short TYPVAL<short>::GetTypedValue(PSZ s) {return (short)atoi(s);} +template <> +longlong TYPVAL<longlong>::GetTypedValue(PSZ s) {return atoll(s);} +template <> +double TYPVAL<double>::GetTypedValue(PSZ s) {return atof(s);} +template <> +char TYPVAL<char>::GetTypedValue(PSZ s) {return (char)atoi(s);} + +/***********************************************************************/ +/* TYPVAL SetValue: set value with a TYPE extracted from a block. */ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::SetValue_pvblk(PVBLK blk, int n) + { + Tval = GetTypedValue(blk, n); + Null = false; + } // end of SetValue + +template <> +int TYPVAL<int>::GetTypedValue(PVBLK blk, int n) + {return blk->GetIntValue(n);} + +template <> +short TYPVAL<short>::GetTypedValue(PVBLK blk, int n) + {return blk->GetShortValue(n);} + +template <> +longlong TYPVAL<longlong>::GetTypedValue(PVBLK blk, int n) + {return blk->GetBigintValue(n);} + +template <> +double TYPVAL<double>::GetTypedValue(PVBLK blk, int n) + {return blk->GetFloatValue(n);} + +template <> +char TYPVAL<char>::GetTypedValue(PVBLK blk, int n) + {return blk->GetTinyValue(n);} + +/***********************************************************************/ +/* TYPVAL SetBinValue: with bytes extracted from a line. */ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::SetBinValue(void *p) + { + Tval = *(TYPE *)p; + Null = false; + } // end of SetBinValue + +/***********************************************************************/ +/* GetBinValue: fill a buffer with the internal binary value. */ +/* This function checks whether the buffer length is enough and */ +/* returns true if not. Actual filling occurs only if go is true. */ +/* Currently used by WriteColumn of binary files. */ +/***********************************************************************/ +template <class TYPE> +bool TYPVAL<TYPE>::GetBinValue(void *buf, int buflen, bool go) + { + // Test on length was removed here until a variable in column give the + // real field length. For BIN files the field length logically cannot + // be different from the variable length because no conversion is done. + // Therefore this test is useless anyway. +//#if defined(_DEBUG) +// if (sizeof(int) > buflen) +// return true; +//#endif + + if (go) + *(TYPE *)buf = Tval; + + Null = false; + return false; + } // end of GetBinValue + +/***********************************************************************/ +/* TYPVAL ShowValue: get string representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::ShowValue(char *buf, int len) + { + sprintf(buf, Xfmt, len, Tval); + return buf; + } // end of ShowValue + +template <> +char *TYPVAL<double>::ShowValue(char *buf, int len) + { + // TODO: use snprintf to avoid possible overflow + sprintf(buf, Xfmt, len, Prec, Tval); + return buf; + } // end of ShowValue + +/***********************************************************************/ +/* TYPVAL GetCharString: get string representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetCharString(char *p) + { + sprintf(p, Fmt, Tval); + return p; + } // end of GetCharString + +template <> +char *TYPVAL<double>::GetCharString(char *p) + { + sprintf(p, Fmt, Prec, Tval); + return p; + } // end of GetCharString + +/***********************************************************************/ +/* TYPVAL GetShortString: get short representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetShortString(char *p, int n) + { + sprintf(p, "%*hd", n, (short)Tval); + return p; + } // end of GetShortString + +/***********************************************************************/ +/* TYPVAL GetIntString: get int representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetIntString(char *p, int n) + { + sprintf(p, "%*d", n, (int)Tval); + return p; + } // end of GetIntString + +/***********************************************************************/ +/* TYPVAL GetBigintString: get big int representation of a TYPE value.*/ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetBigintString(char *p, int n) + { + sprintf(p, "%*lld", n, (longlong)Tval); + return p; + } // end of GetBigintString + +/***********************************************************************/ +/* TYPVAL GetFloatString: get double representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetFloatString(char *p, int n, int prec) + { + sprintf(p, "%*.*lf", n, (prec < 0) ? 2 : prec, (double)Tval); + return p; + } // end of GetFloatString + +/***********************************************************************/ +/* TYPVAL GetTinyString: get char representation of a typed value. */ +/***********************************************************************/ +template <class TYPE> +char *TYPVAL<TYPE>::GetTinyString(char *p, int n) + { + sprintf(p, "%*d", n, (int)(char)Tval); + return p; + } // end of GetIntString + +/***********************************************************************/ +/* TYPVAL compare value with another Value. */ +/***********************************************************************/ +template <class TYPE> +bool TYPVAL<TYPE>::IsEqual(PVAL vp, bool chktype) + { + if (this == vp) + return true; + else if (chktype && Type != vp->GetType()) + return false; + else if (Null || vp->IsNull()) + return false; + else + return (Tval == GetTypedValue(vp)); + + } // end of IsEqual + +/***********************************************************************/ +/* FormatValue: This function set vp (a STRING value) to the string */ +/* constructed from its own value formated using the fmt format. */ +/* This function assumes that the format matches the value type. */ +/***********************************************************************/ +template <class TYPE> +bool TYPVAL<TYPE>::FormatValue(PVAL vp, char *fmt) + { + char *buf = (char*)vp->GetTo_Val(); // Should be big enough + int n = sprintf(buf, fmt, Tval); + + return (n > vp->GetValLen()); + } // end of FormatValue + +/***********************************************************************/ +/* TYPVAL SetFormat function (used to set SELECT output format). */ +/***********************************************************************/ +template <class TYPE> +bool TYPVAL<TYPE>::SetConstFormat(PGLOBAL g, FORMAT& fmt) + { + char c[32]; + + fmt.Type[0] = *GetFormatType(Type); + fmt.Length = sprintf(c, Fmt, Tval); + fmt.Prec = Prec; + return false; + } // end of SetConstFormat + +/***********************************************************************/ +/* Make file output of a typed object. */ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::Print(PGLOBAL g, FILE *f, uint n) + { + char m[64], buf[12]; + + memset(m, ' ', n); /* Make margin string */ + m[n] = '\0'; + + if (Null) + fprintf(f, "%s<null>\n", m); + else + fprintf(f, strcat(strcat(strcpy(buf, "%s"), Fmt), "\n"), m, Tval); + + } /* end of Print */ + +/***********************************************************************/ +/* Make string output of a int object. */ +/***********************************************************************/ +template <class TYPE> +void TYPVAL<TYPE>::Print(PGLOBAL g, char *ps, uint z) + { + if (Null) + strcpy(ps, "<null>"); + else + sprintf(ps, Fmt, Tval); + + } /* end of Print */ + +/* -------------------------- Class STRING --------------------------- */ + +/***********************************************************************/ +/* STRING public constructor from a constant string. */ +/***********************************************************************/ +TYPVAL<PSZ>::TYPVAL(PSZ s) : VALUE(TYPE_STRING) + { + Strp = s; + Len = strlen(s); + Clen = Len; + Ci = false; + } // end of STRING constructor + +/***********************************************************************/ +/* STRING public constructor from char. */ +/***********************************************************************/ +TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c) + : VALUE(TYPE_STRING) + { + assert(Type == TYPE_STRING && (g || s)); + Len = (g) ? n : strlen(s); + + if (g && !s) { + Strp = (char *)PlugSubAlloc(g, NULL, Len + 1); + Strp[Len] = '\0'; + } else + Strp = s; + + Clen = Len; + Ci = (c != 0); + } // end of STRING constructor + +/***********************************************************************/ +/* STRING SetValue: copy the value of another Value object. */ +/***********************************************************************/ +bool TYPVAL<PSZ>::SetValue_pval(PVAL valp, bool chktype) + { + if (chktype && (valp->GetType() != Type || valp->GetSize() > Len)) + return true; + + char buf[32]; + + if (!(Null = valp->IsNull() && Nullable)) + strncpy(Strp, valp->GetCharString(buf), Len); + else + Reset(); + + return false; + } // end of SetValue_pval + +/***********************************************************************/ +/* STRING SetValue: fill string with chars extracted from a line. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue_char(char *p, int n) + { + n = min(n, Len); + strncpy(Strp, p, n); + + for (p = Strp + n - 1; (*p == ' ' || *p == '\0') && p >= Strp; p--) ; + + *(++p) = '\0'; + + if (trace) + htrc(" Setting string to: '%s'\n", Strp); + + Null = false; + } // end of SetValue_char + +/***********************************************************************/ +/* STRING SetValue: fill string with another string. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue_psz(PSZ s) + { + strncpy(Strp, s, Len); + Null = false; + } // end of SetValue_psz + +/***********************************************************************/ +/* STRING SetValue: fill string with a string extracted from a block. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue_pvblk(PVBLK blk, int n) + { + strncpy(Strp, blk->GetCharValue(n), Len); + } // end of SetValue_pvblk + +/***********************************************************************/ +/* STRING SetValue: get the character representation of an integer. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue(int n) + { + char buf[16]; + PGLOBAL& g = Global; + int k = sprintf(buf, "%d", n); + + if (k > Len) { + sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len); + longjmp(g->jumper[g->jump_level], 138); + } else + SetValue_psz(buf); + + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a short int. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue(short i) + { + SetValue((int)i); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a big integer.*/ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue(longlong n) + { + char buf[24]; + PGLOBAL& g = Global; + int k = sprintf(buf, "%lld", n); + + if (k > Len) { + sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len); + longjmp(g->jumper[g->jump_level], 138); + } else + SetValue_psz(buf); + + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a double. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue(double f) + { + char *p, buf[32]; + PGLOBAL& g = Global; + int k = sprintf(buf, "%lf", f); + + for (p = buf + k - 1; p >= buf; p--) + if (*p == '0') { + *p = 0; + k--; + } else + break; + + if (k > Len) { + sprintf(g->Message, MSG(VALSTR_TOO_LONG), buf, Len); + longjmp(g->jumper[g->jump_level], 138); + } else + SetValue_psz(buf); + + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetValue: get the character representation of a tiny int. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetValue(char c) + { + SetValue((int)c); + Null = false; + } // end of SetValue + +/***********************************************************************/ +/* STRING SetBinValue: fill string with chars extracted from a line. */ +/***********************************************************************/ +void TYPVAL<PSZ>::SetBinValue(void *p) + { + SetValue_char((char *)p, Len); + Null = false; + } // end of SetBinValue + +/***********************************************************************/ +/* GetBinValue: fill a buffer with the internal binary value. */ +/* This function checks whether the buffer length is enough and */ +/* returns true if not. Actual filling occurs only if go is true. */ +/* Currently used by WriteColumn of binary files. */ +/***********************************************************************/ +bool TYPVAL<PSZ>::GetBinValue(void *buf, int buflen, bool go) + { + int len = (Null) ? 0 : strlen(Strp); + + if (len > buflen) + return true; + else if (go) { + memset(buf, ' ', buflen); + memcpy(buf, Strp, len); + } // endif go + + return false; + } // end of GetBinValue + +/***********************************************************************/ +/* STRING ShowValue: get string representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::ShowValue(char *buf, int len) + { + return Strp; + } // end of ShowValue + +/***********************************************************************/ +/* STRING GetCharString: get string representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetCharString(char *p) + { + return Strp; + } // end of GetCharString + +/***********************************************************************/ +/* STRING GetShortString: get short representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetShortString(char *p, int n) + { + sprintf(p, "%*hd", n, (short)(Null ? 0 : atoi(Strp))); + return p; + } // end of GetShortString + +/***********************************************************************/ +/* STRING GetIntString: get int representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetIntString(char *p, int n) + { + sprintf(p, "%*ld", n, (Null) ? 0 : atol(Strp)); + return p; + } // end of GetIntString + +/***********************************************************************/ +/* STRING GetBigintString: get big int representation of a char value.*/ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetBigintString(char *p, int n) + { + sprintf(p, "%*lld", n, (Null) ? 0 : atoll(Strp)); + return p; + } // end of GetBigintString + +/***********************************************************************/ +/* STRING GetFloatString: get double representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetFloatString(char *p, int n, int prec) + { + sprintf(p, "%*.*lf", n, (prec < 0) ? 2 : prec, Null ? 0 : atof(Strp)); + return p; + } // end of GetFloatString + +/***********************************************************************/ +/* STRING GetTinyString: get tiny int representation of a char value. */ +/***********************************************************************/ +char *TYPVAL<PSZ>::GetTinyString(char *p, int n) + { + sprintf(p, "%*d", n, (Null) ? 0 : (char)atoi(Strp)); + return p; + } // end of GetIntString + +/***********************************************************************/ +/* STRING compare value with another Value. */ +/***********************************************************************/ +bool TYPVAL<PSZ>::IsEqual(PVAL vp, bool chktype) + { + if (this == vp) + return true; + else if (chktype && Type != vp->GetType()) + return false; + else if (Null || vp->IsNull()) + return false; + else if (Ci || vp->IsCi()) + return !stricmp(Strp, vp->GetCharValue()); + else // (!Ci) + return !strcmp(Strp, vp->GetCharValue()); + + } // end of IsEqual + +/***********************************************************************/ +/* FormatValue: This function set vp (a STRING value) to the string */ +/* constructed from its own value formated using the fmt format. */ +/* This function assumes that the format matches the value type. */ +/***********************************************************************/ +bool TYPVAL<PSZ>::FormatValue(PVAL vp, char *fmt) + { + char *buf = (char*)vp->GetTo_Val(); // Should be big enough + int n = sprintf(buf, fmt, Strp); + + return (n > vp->GetValLen()); + } // end of FormatValue + +/***********************************************************************/ +/* STRING SetFormat function (used to set SELECT output format). */ +/***********************************************************************/ +bool TYPVAL<PSZ>::SetConstFormat(PGLOBAL g, FORMAT& fmt) + { + fmt.Type[0] = 'C'; + fmt.Length = Len; + fmt.Prec = 0; + return false; + } // end of SetConstFormat + +/* -------------------------- Class DTVAL ---------------------------- */ + +/***********************************************************************/ +/* DTVAL public constructor for new void values. */ +/***********************************************************************/ +DTVAL::DTVAL(PGLOBAL g, int n, int prec, PSZ fmt) + : TYPVAL<int>((int)0, TYPE_DATE) + { + if (!fmt) { + Pdtp = NULL; + Sdate = NULL; + DefYear = 0; + Len = n; + } else + SetFormat(g, fmt, n, prec); + +//Type = TYPE_DATE; + } // end of DTVAL constructor + +/***********************************************************************/ +/* DTVAL public constructor from int. */ +/***********************************************************************/ +DTVAL::DTVAL(PGLOBAL g, int n) : TYPVAL<int>(n, TYPE_DATE) + { + Pdtp = NULL; + Len = 19; +//Type = TYPE_DATE; + Sdate = NULL; + DefYear = 0; + } // end of DTVAL constructor + +/***********************************************************************/ +/* Set format so formatted dates can be converted on input/output. */ +/***********************************************************************/ +bool DTVAL::SetFormat(PGLOBAL g, PSZ fmt, int len, int year) + { + Pdtp = MakeDateFormat(g, fmt, true, true, (year > 9999) ? 1 : 0); + Sdate = (char*)PlugSubAlloc(g, NULL, len + 1); + DefYear = (int)((year > 9999) ? (year - 10000) : year); + Len = len; + return false; + } // end of SetFormat + +/***********************************************************************/ +/* Set format from the format of another date value. */ +/***********************************************************************/ +bool DTVAL::SetFormat(PGLOBAL g, PVAL valp) + { + DTVAL *vp; + + if (valp->GetType() != TYPE_DATE) { + sprintf(g->Message, MSG(NO_FORMAT_TYPE), valp->GetType()); + return true; + } else + vp = (DTVAL*)valp; + + Len = vp->Len; + Pdtp = vp->Pdtp; + Sdate = (char*)PlugSubAlloc(g, NULL, Len + 1); + DefYear = vp->DefYear; + return false; + } // end of SetFormat + +/***********************************************************************/ +/* We need TimeShift because the mktime C function does a correction */ +/* for local time zone that we want to override for DB operations. */ +/***********************************************************************/ +void DTVAL::SetTimeShift(void) + { +#if defined(WIN32) + struct tm dtm = {0,0,0,2,0,70,0,0,0}; +#else // !WIN32 + struct tm dtm = {0,0,0,2,0,70,0,0,0,0,0}; +#endif // !WIN32 + + Shift = (int)mktime(&dtm) - 86400; + + if (trace) + htrc("DTVAL Shift=%d\n", Shift); + + } // end of SetTimeShift + +/***********************************************************************/ +/* GetGmTime: returns a pointer to a static tm structure obtained */ +/* though the gmtime C function. The purpose of this function is to */ +/* extend the range of valid dates by accepting negative time values. */ +/***********************************************************************/ +#define MYSQL_SERVER 1 +#include "tztime.h" +#include "sql_priv.h" +#include "sql_class.h" +#include "sql_time.h" + +static void TIME_to_localtime(struct tm *tm, const MYSQL_TIME *ltime) +{ + bzero(tm, sizeof(*tm)); + tm->tm_year= ltime->year - 1900; + tm->tm_mon= ltime->month - 1; + tm->tm_mday= ltime->day; + tm->tm_hour= ltime->hour; + tm->tm_min= ltime->minute; + tm->tm_sec= ltime->second; +} + + +static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm) +{ + MYSQL_TIME ltime; + current_thd->variables.time_zone->gmt_sec_to_TIME(<ime, (my_time_t) *timep); + TIME_to_localtime(tm, <ime); + return tm; +} + + +struct tm *DTVAL::GetGmTime(void) + { + static struct tm tm_static; /* TODO: Move as a parameter to GetGmTime() */ + struct tm *datm; + time_t t = (time_t)Tval; + + if (Tval < 0) { + int n; + + for (n = 0; t < 0; n += 4) + t += FOURYEARS; + + datm = gmtime_mysql(&t, &tm_static); + + if (datm) + datm->tm_year -= n; + + } else + datm = gmtime_mysql(&t, &tm_static); + + return datm; + } // end of GetGmTime + +/***********************************************************************/ +/* MakeTime: calculates a date value from a tm structures using the */ +/* mktime C function. The purpose of this function is to extend the */ +/* range of valid dates by accepting to set negative time values. */ +/***********************************************************************/ + +static time_t mktime_mysql(struct tm *ptm) +{ + MYSQL_TIME ltime; + localtime_to_TIME(<ime, ptm); + ltime.time_type= MYSQL_TIMESTAMP_DATETIME; + uint error_code; + time_t t= TIME_to_timestamp(current_thd, <ime, &error_code); + return error_code ? (time_t) -1 : t; +} + +bool DTVAL::MakeTime(struct tm *ptm) + { + int n, y = ptm->tm_year; + time_t t = mktime_mysql(ptm); + + if (trace) + htrc("MakeTime from (%d,%d,%d,%d,%d,%d)\n", + ptm->tm_year, ptm->tm_mon, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec); + + if (t == -1) { + if (y < 1 || y > 71) + return true; + + for (n = 0; t == -1 && n < 20; n++) { + ptm->tm_year += 4; + t = mktime_mysql(ptm); + } // endfor t + + if (t == -1) + return true; + + if ((t -= (n * FOURYEARS)) > 2000000000) + return true; + + } + Tval= (int) t; + + if (trace) + htrc("MakeTime Ival=%d\n", Tval); + + return false; + } // end of MakeTime + +/***********************************************************************/ +/* Make a time_t datetime from its components (YY, MM, DD, hh, mm, ss) */ +/***********************************************************************/ +bool DTVAL::MakeDate(PGLOBAL g, int *val, int nval) + { + int i, m; + int n; + bool rc = false; +#if defined(WIN32) + struct tm datm = {0,0,0,1,0,70,0,0,0}; +#else // !WIN32 + struct tm datm = {0,0,0,1,0,70,0,0,0,0,0}; +#endif // !WIN32 + + if (trace) + htrc("MakeDate from(%d,%d,%d,%d,%d,%d) nval=%d\n", + val[0], val[1], val[2], val[3], val[4], val[5], nval); + + for (i = 0; i < nval; i++) { + n = val[i]; + +// if (trace > 1) +// htrc("i=%d n=%d\n", i, n); + + switch (i) { + case 0: + if (n >= 1900) + n -= 1900; + + datm.tm_year = n; + +// if (trace > 1) +// htrc("n=%d tm_year=%d\n", n, datm.tm_year); + + break; + case 1: + // If mktime handles apparently correctly large or negative + // day values, it is not the same for months. Therefore we + // do the ajustment here, thus mktime has not to do it. + if (n > 0) { + m = (n - 1) % 12; + n = (n - 1) / 12; + } else { + m = 11 + n % 12; + n = n / 12 - 1; + } // endfi n + + datm.tm_mon = m; + datm.tm_year += n; + +// if (trace > 1) +// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon); + + break; + case 2: + // For days, big or negative values may also cause problems + m = n % 1461; + n = 4 * (n / 1461); + + if (m < 0) { + m += 1461; + n -= 4; + } // endif m + + datm.tm_mday = m; + datm.tm_year += n; + +// if (trace > 1) +// htrc("n=%d m=%d tm_year=%d tm_mon=%d\n", n, m, datm.tm_year, datm.tm_mon); + + break; + case 3: datm.tm_hour = n; break; + case 4: datm.tm_min = n; break; + case 5: datm.tm_sec = n; break; + } // endswitch i + + } // endfor i + + if (trace) + htrc("MakeDate datm=(%d,%d,%d,%d,%d,%d)\n", + datm.tm_year, datm.tm_mon, datm.tm_mday, + datm.tm_hour, datm.tm_min, datm.tm_sec); + + // Pass g to have an error return or NULL to set invalid dates to 0 + if (MakeTime(&datm)) + if (g) { + strcpy(g->Message, MSG(BAD_DATETIME)); + rc = true; + } else + Tval = 0; + + return rc; + } // end of MakeDate + +/***********************************************************************/ +/* DTVAL SetValue: copy the value of another Value object. */ +/* This function allows conversion if chktype is false. */ +/***********************************************************************/ +bool DTVAL::SetValue_pval(PVAL valp, bool chktype) + { + if (chktype && Type != valp->GetType()) + return true; + + if (!(Null = valp->IsNull() && Nullable)) { + if (Pdtp && !valp->IsTypeNum()) { + int ndv; + int dval[6]; + + ndv = ExtractDate(valp->GetCharValue(), Pdtp, DefYear, dval); + MakeDate(NULL, dval, ndv); + } else + Tval = valp->GetIntValue(); + + } else + Reset(); + + return false; + } // end of SetValue + +/***********************************************************************/ +/* SetValue: convert chars extracted from a line to date value. */ +/***********************************************************************/ +void DTVAL::SetValue_char(char *p, int n) + { + if (Pdtp) { + char *p2; + int ndv; + int dval[6]; + + // Trim trailing blanks + for (p2 = p + n -1; p < p2 && *p2 == ' '; p2--) ; + + n = min(p2 - p + 1, Len); + memcpy(Sdate, p, n); + Sdate[n] = '\0'; + + ndv = ExtractDate(Sdate, Pdtp, DefYear, dval); + MakeDate(NULL, dval, ndv); + + if (trace) + htrc(" setting date: '%s' -> %d\n", Sdate, Tval); + + Null = false; + } else + TYPVAL<int>::SetValue_char(p, n); + + } // end of SetValue + +/***********************************************************************/ +/* SetValue: convert a char string to date value. */ +/***********************************************************************/ +void DTVAL::SetValue_psz(PSZ p) + { + if (Pdtp) { + int ndv; + int dval[6]; + + strncpy(Sdate, p, Len); + Sdate[Len] = '\0'; + + ndv = ExtractDate(Sdate, Pdtp, DefYear, dval); + MakeDate(NULL, dval, ndv); + + if (trace) + htrc(" setting date: '%s' -> %d\n", Sdate, Tval); + + Null = false; + } else + TYPVAL<int>::SetValue_psz(p); + + } // end of SetValue + +/***********************************************************************/ +/* DTVAL SetValue: set value with a value extracted from a block. */ +/***********************************************************************/ +void DTVAL::SetValue_pvblk(PVBLK blk, int n) + { + if (Pdtp && !::IsTypeNum(blk->GetType())) { + int ndv; + int dval[6]; + + ndv = ExtractDate(blk->GetCharValue(n), Pdtp, DefYear, dval); + MakeDate(NULL, dval, ndv); + } else + Tval = blk->GetIntValue(n); + + } // end of SetValue + +/***********************************************************************/ +/* DTVAL GetCharString: get string representation of a date value. */ +/***********************************************************************/ +char *DTVAL::GetCharString(char *p) + { + if (Pdtp) { + size_t n = 0; + struct tm *ptm = GetGmTime(); + + if (ptm) + n = strftime(Sdate, Len + 1, Pdtp->OutFmt, ptm); + + if (!n) { + *Sdate = '\0'; + strncat(Sdate, "Error", Len + 1); + } // endif n + + return Sdate; + } else + sprintf(p, "%d", Tval); + + Null = false; + return p; + } // end of GetCharString + +/***********************************************************************/ +/* DTVAL ShowValue: get string representation of a date value. */ +/***********************************************************************/ +char *DTVAL::ShowValue(char *buf, int len) + { + if (Pdtp) { + char *p; + size_t m, n = 0; + struct tm *ptm = GetGmTime(); + + if (Len < len) { + p = buf; + m = len; + } else { + p = Sdate; + m = Len + 1; + } // endif Len + + if (ptm) + n = strftime(p, m, Pdtp->OutFmt, ptm); + + if (!n) { + *p = '\0'; + strncat(p, "Error", m); + } // endif n + + return p; + } else + return TYPVAL<int>::ShowValue(buf, len); + + } // end of ShowValue + +/***********************************************************************/ +/* Returns a member of the struct tm representation of the date. */ +/***********************************************************************/ +bool DTVAL::GetTmMember(OPVAL op, int& mval) + { + bool rc = false; + struct tm *ptm = GetGmTime(); + + switch (op) { + case OP_MDAY: mval = ptm->tm_mday; break; + case OP_MONTH: mval = ptm->tm_mon + 1; break; + case OP_YEAR: mval = ptm->tm_year + 1900; break; + case OP_WDAY: mval = ptm->tm_wday + 1; break; + case OP_YDAY: mval = ptm->tm_yday + 1; break; + case OP_QUART: mval = ptm->tm_mon / 3 + 1; break; + default: + rc = true; + } // endswitch op + + return rc; + } // end of GetTmMember + +/***********************************************************************/ +/* Calculates the week number of the year for the internal date value.*/ +/* The International Standard ISO 8601 has decreed that Monday shall */ +/* be the first day of the week. A week that lies partly in one year */ +/* and partly in another is assigned a number in the year in which */ +/* most of its days lie. That means that week number 1 of any year is */ +/* the week that contains the January 4th. */ +/***********************************************************************/ +bool DTVAL::WeekNum(PGLOBAL g, int& nval) + { + // w is the start of the week SUN=0, MON=1, etc. + int m, n, w = nval % 7; + struct tm *ptm = GetGmTime(); + + // Which day is January 4th of this year? + m = (367 + ptm->tm_wday - ptm->tm_yday) % 7; + + // When does the first week begins? + n = 3 - (7 + m - w) % 7; + + // Now calculate the week number + if (!(nval = (7 + ptm->tm_yday - n) / 7)) + nval = 52; + + // Everything should be Ok + return false; + } // end of WeekNum + +/***********************************************************************/ +/* FormatValue: This function set vp (a STRING value) to the string */ +/* constructed from its own value formated using the fmt format. */ +/* This function assumes that the format matches the value type. */ +/***********************************************************************/ +bool DTVAL::FormatValue(PVAL vp, char *fmt) + { + char *buf = (char*)vp->GetTo_Val(); // Should be big enough + struct tm *ptm = GetGmTime(); + + if (trace) + htrc("FormatValue: ptm=%p len=%d\n", ptm, vp->GetValLen()); + + if (ptm) { + size_t n = strftime(buf, vp->GetValLen(), fmt, ptm); + + if (trace) + htrc("strftime: n=%d buf=%s\n", n, (n) ? buf : "???"); + + return (n == 0); + } else + return true; + + } // end of FormatValue + +/* -------------------------- End of Value --------------------------- */ diff --git a/storage/connect/value.h b/storage/connect/value.h new file mode 100644 index 00000000000..0f7b795c252 --- /dev/null +++ b/storage/connect/value.h @@ -0,0 +1,306 @@ +/**************** Value H Declares Source Code File (.H) ***************/ +/* Name: VALUE.H Version 1.9 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2013 */ +/* */ +/* This file contains the VALUE and derived classes declares. */ +/***********************************************************************/ +#ifndef __VALUE__H__ +#define __VALUE__H__ + +/***********************************************************************/ +/* Include required application header files */ +/* assert.h is header required when using the assert function. */ +/* block.h is header containing Block global declarations. */ +/***********************************************************************/ +#include "assert.h" +#include "block.h" + +#if defined(WIN32) +#define strtoll _strtoi64 +#define atoll(S) strtoll(S, NULL, 10) +#endif // WIN32 + +/***********************************************************************/ +/* Types used in some class definitions. */ +/***********************************************************************/ +enum CONV {CNV_ANY = 0, /* Convert to any type */ + CNV_CHAR = 1, /* Convert to character type */ + CNV_NUM = 2}; /* Convert to numeric type */ + +/***********************************************************************/ +/* Types used in some class definitions. */ +/***********************************************************************/ +class CONSTANT; // For friend setting +typedef struct _datpar *PDTP; // For DTVAL + + +/***********************************************************************/ +/* Utilities used to test types and to allocated values. */ +/***********************************************************************/ +int GetPLGType(int); +PVAL AllocateValue(PGLOBAL, void *, short); + +// Exported functions +DllExport PSZ GetTypeName(int); +DllExport int GetTypeSize(int, int); +#ifdef ODBC_SUPPORT +/* This function is exported for use in EOM table type DLLs */ +DllExport int TranslateSQLType(int stp, int prec, int& len); +#endif +DllExport char *GetFormatType(int); +DllExport int GetFormatType(char); +DllExport int GetDBType(int); +DllExport bool IsTypeChar(int type); +DllExport bool IsTypeNum(int type); +DllExport int ConvertType(int, int, CONV, bool match = false); +DllExport PVAL AllocateValue(PGLOBAL, PVAL, int = TYPE_VOID); +DllExport PVAL AllocateValue(PGLOBAL, int, int len = 0, int prec = 2, + PSZ dom = NULL, PCATLG cat = NULL); + +/***********************************************************************/ +/* Class VALUE represents a constant or variable of any valid type. */ +/***********************************************************************/ +class DllExport VALUE : public BLOCK { + friend class CONSTANT; // The only object allowed to use SetConstFormat + public: + // Constructors + + // Implementation + virtual bool IsTypeNum(void) = 0; + virtual bool IsZero(void) = 0; + virtual bool IsCi(void) {return false;} + virtual void Reset(void) = 0; + virtual int GetSize(void) = 0; + virtual int GetValLen(void) = 0; + virtual int GetValPrec(void) = 0; + virtual int GetLength(void) {return 1;} + virtual PSZ GetCharValue(void) {assert(false); return NULL;} + virtual char GetTinyValue(void) {assert(false); return 0;} + virtual short GetShortValue(void) {assert(false); return 0;} + virtual int GetIntValue(void) = 0; + virtual longlong GetBigintValue(void) = 0; + virtual double GetFloatValue(void) = 0; + virtual void *GetTo_Val(void) = 0; + bool IsNull(void) {return Null;} + void SetNull(bool b) {Null = b;} + void SetNullable(bool b) {Nullable = b;} + int GetType(void) {return Type;} + int GetClen(void) {return Clen;} + void SetPrec(int prec) {Prec = prec;} + void SetGlobal(PGLOBAL g) {Global = g;} + + // Methods + virtual bool SetValue_pval(PVAL valp, bool chktype = false) = 0; + virtual void SetValue_char(char *p, int n) = 0; + virtual void SetValue_psz(PSZ s) = 0; + virtual void SetValue(char c) {assert(false);} + virtual void SetValue(short i) {assert(false);} + virtual void SetValue(int n) {assert(false);} + virtual void SetValue(longlong n) {assert(false);} + virtual void SetValue(double f) {assert(false);} + virtual void SetValue_pvblk(PVBLK blk, int n) = 0; + virtual void SetBinValue(void *p) = 0; + virtual bool GetBinValue(void *buf, int buflen, bool go) = 0; + virtual char *ShowValue(char *buf, int len = 0) = 0; + virtual char *GetCharString(char *p) = 0; + virtual char *GetShortString(char *p, int n) {return "#####";} + virtual char *GetIntString(char *p, int n) = 0; + virtual char *GetBigintString(char *p, int n) = 0; + virtual char *GetFloatString(char *p, int n, int prec) = 0; + virtual char *GetTinyString(char *p, int n) {return "?";} + virtual bool IsEqual(PVAL vp, bool chktype) = 0; + virtual bool FormatValue(PVAL vp, char *fmt) = 0; + + protected: + virtual bool SetConstFormat(PGLOBAL, FORMAT&) = 0; + const char *GetXfmt(void); + + // Constructor used by derived classes + VALUE(int type); + + // Members + PGLOBAL Global; // To reduce arglist + const char *Fmt; + const char *Xfmt; + bool Nullable; // True if value can be null + bool Null; // True if value is null + int Type; // The value type + int Clen; // Internal value length + int Prec; + }; // end of class VALUE + +/***********************************************************************/ +/* Class TYPVAL: represents a typed value. */ +/***********************************************************************/ +template <class TYPE> +class DllExport TYPVAL : public VALUE { + public: + // Constructors + TYPVAL(TYPE n, int type); + TYPVAL(TYPE n, int prec, int type); + + // Implementation + virtual bool IsTypeNum(void) {return true;} + virtual bool IsZero(void) {return Tval == 0;} + virtual void Reset(void) {Tval = 0;} + virtual int GetValLen(void); + virtual int GetValPrec() {return 0;} + virtual int GetSize(void) {return sizeof(TYPE);} + virtual PSZ GetCharValue(void) {return VALUE::GetCharValue();} + virtual char GetTinyValue(void) {return (char)Tval;} + virtual short GetShortValue(void) {return (short)Tval;} + virtual int GetIntValue(void) {return (int)Tval;} + virtual longlong GetBigintValue(void) {return (longlong)Tval;} + virtual double GetFloatValue(void) {return (double)Tval;} + virtual void *GetTo_Val(void) {return &Tval;} + + // Methods + virtual bool SetValue_pval(PVAL valp, bool chktype); + virtual void SetValue_char(char *p, int n); + virtual void SetValue_psz(PSZ s); + virtual void SetValue(char c) {Tval = (TYPE)c; Null = false;} + virtual void SetValue(short i) {Tval = (TYPE)i; Null = false;} + virtual void SetValue(int n) {Tval = (TYPE)n; Null = false;} + virtual void SetValue(longlong n) {Tval = (TYPE)n; Null = false;} + virtual void SetValue(double f) {Tval = (TYPE)f; Null = false;} + virtual void SetValue_pvblk(PVBLK blk, int n); + virtual void SetBinValue(void *p); + virtual bool GetBinValue(void *buf, int buflen, bool go); + virtual char *ShowValue(char *buf, int); + virtual char *GetCharString(char *p); + virtual char *GetShortString(char *p, int n); + virtual char *GetIntString(char *p, int n); + virtual char *GetBigintString(char *p, int n); + virtual char *GetFloatString(char *p, int n, int prec = -1); + virtual char *GetTinyString(char *p, int n); + virtual bool IsEqual(PVAL vp, bool chktype); + virtual bool SetConstFormat(PGLOBAL, FORMAT&); + virtual bool FormatValue(PVAL vp, char *fmt); + virtual void Print(PGLOBAL g, FILE *, uint); + virtual void Print(PGLOBAL g, char *, uint); + + protected: + // Default constructor not to be used + TYPVAL(void) : VALUE(TYPE_ERROR) {} + + // Specialized functions + TYPE GetTypedValue(PVAL vp); + TYPE GetTypedValue(PVBLK blk, int n); + TYPE GetTypedValue(PSZ s); + + // Members + TYPE Tval; + }; // end of class TYPVAL + +/***********************************************************************/ +/* Specific STRING class. */ +/***********************************************************************/ +template <> +class DllExport TYPVAL<PSZ>: public VALUE { + public: + // Constructors + TYPVAL(PSZ s); + TYPVAL(PGLOBAL g, PSZ s, int n, int c); + + // Implementation + virtual bool IsTypeNum(void) {return false;} + virtual bool IsZero(void) {return *Strp == 0;} + virtual void Reset(void) {*Strp = 0;} + virtual int GetValLen(void) {return Len;}; + virtual int GetValPrec() {return (Ci) ? 1 : 0;} + virtual int GetSize(void) {return (Strp) ? strlen(Strp) : 0;} + virtual PSZ GetCharValue(void) {return Strp;} + virtual char GetTinyValue(void) {return (char)atoi(Strp);} + virtual short GetShortValue(void) {return (short)atoi(Strp);} + virtual int GetIntValue(void) {return atol(Strp);} + virtual longlong GetBigintValue(void) {return atoll(Strp);} + virtual double GetFloatValue(void) {return atof(Strp);} + virtual void *GetTo_Val(void) {return Strp;} + + // Methods + virtual bool SetValue_pval(PVAL valp, bool chktype); + virtual void SetValue_char(char *p, int n); + virtual void SetValue_psz(PSZ s); + virtual void SetValue_pvblk(PVBLK blk, int n); + virtual void SetValue(char c); + virtual void SetValue(short i); + virtual void SetValue(int n); + virtual void SetValue(longlong n); + virtual void SetValue(double f); + virtual void SetBinValue(void *p); + virtual bool GetBinValue(void *buf, int buflen, bool go); + virtual char *ShowValue(char *buf, int); + virtual char *GetCharString(char *p); + virtual char *GetShortString(char *p, int n); + virtual char *GetIntString(char *p, int n); + virtual char *GetBigintString(char *p, int n); + virtual char *GetFloatString(char *p, int n, int prec = -1); + virtual char *GetTinyString(char *p, int n); + virtual bool IsEqual(PVAL vp, bool chktype); + virtual bool FormatValue(PVAL vp, char *fmt); + virtual bool SetConstFormat(PGLOBAL, FORMAT&); + + // Specialized functions + template <class T> + T GetValue_as(T type) {return Strp;} + int GetValue_as(int type) {return atol(Strp);} + short GetValue_as(short type) {return (short)atoi(Strp);} + longlong GetValue_as(longlong type) {return atoll(Strp);} + double GetValue_as(double type) {return atof(Strp);} + + // Members + PSZ Strp; + bool Ci; // true if case insensitive + int Len; + }; // end of class TYPVAL<PSZ> + +/***********************************************************************/ +/* Class DTVAL: represents a time stamp value. */ +/***********************************************************************/ +class DllExport DTVAL : public TYPVAL<int> { + public: + // Constructors + DTVAL(PGLOBAL g, int n, int p, PSZ fmt); + DTVAL(PGLOBAL g, PSZ s, int n); + DTVAL(PGLOBAL g, short i); + DTVAL(PGLOBAL g, int n); + DTVAL(PGLOBAL g, longlong n); + DTVAL(PGLOBAL g, double f); + + // Implementation + virtual bool SetValue_pval(PVAL valp, bool chktype); + virtual void SetValue_char(char *p, int n); + virtual void SetValue_psz(PSZ s); + virtual void SetValue_pvblk(PVBLK blk, int n); + virtual char *GetCharString(char *p); + virtual char *ShowValue(char *buf, int); + virtual bool FormatValue(PVAL vp, char *fmt); + bool SetFormat(PGLOBAL g, PSZ fmt, int len, int year = 0); + bool SetFormat(PGLOBAL g, PVAL valp); + bool IsFormatted(void) {return Pdtp != NULL;} + bool GetTmMember(OPVAL op, int& mval); + bool DateDiff(DTVAL *dtp, OPVAL op, int& tdif); + bool MakeTime(struct tm *ptm); + static void SetTimeShift(void); + static int GetShift(void) {return Shift;} + + // Methods + bool MakeDate(PGLOBAL g, int *val, int nval); + bool WeekNum(PGLOBAL g, int& nval); + + struct tm *GetGmTime(void); + + protected: + // Default constructor not to be used + DTVAL(void) : TYPVAL<int>() {} + + // Members + static int Shift; // Time zone shift in seconds + PDTP Pdtp; // To the DATPAR structure + char *Sdate; // Utility char buffer + int DefYear; // Used by ExtractDate + int Len; // Used by CHAR scalar function + }; // end of class DTVAL + +#endif // __VALUE__H__ diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp new file mode 100755 index 00000000000..68a6bba47ec --- /dev/null +++ b/storage/connect/xindex.cpp @@ -0,0 +1,3022 @@ +/***************** Xindex C++ Class Xindex Code (.CPP) *****************/ +/* Name: XINDEX.CPP Version 2.8 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2013 */ +/* */ +/* This file contains the class XINDEX implementation code. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if defined(WIN32) +#include <io.h> +#include <fcntl.h> +#include <errno.h> +//#include <windows.h> +#else // !WIN32 +#if defined(UNIX) +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#else // !UNIX +#include <io.h> +#endif // !UNIX +#include <fcntl.h> +#endif // !WIN32 + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/* kindex.h is header containing the KINDEX class definition. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "osutil.h" +#include "maputil.h" +//nclude "filter.h" +#include "tabcol.h" +#include "xindex.h" +#include "xobject.h" +//nclude "scalfnc.h" +//nclude "array.h" +#include "filamtxt.h" +#include "tabdos.h" + +/***********************************************************************/ +/* Macro or external routine definition */ +/***********************************************************************/ +#define NZ 7 +#define NW 5 +#define MAX_INDX 10 +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF +#endif + +/***********************************************************************/ +/* DB static external variables. */ +/***********************************************************************/ +extern MBLOCK Nmblk; /* Used to initialize MBLOCK's */ + +/***********************************************************************/ +/* Last two parameters are true to enable type checking, and last one */ +/* to have rows filled by blanks to be compatible with QRY blocks. */ +/***********************************************************************/ +PVBLK AllocValBlock(PGLOBAL, void *, int, int, int, int, + bool check = true, bool blank = true); + +/***********************************************************************/ +/* Check whether we have to create/update permanent indexes. */ +/***********************************************************************/ +int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add) + { + int rc; + PTABLE tablep; + PTDBDOS tdbp; + PCATLG cat = PlgGetCatalog(g, true); + + /*********************************************************************/ + /* Open a new table in mode read and with only the keys columns. */ + /*********************************************************************/ + tablep = new(g) XTAB(name); + + if (!(tdbp = (PTDBDOS)cat->GetTable(g, tablep))) + rc = RC_NF; + else if (!tdbp->GetDef()->Indexable()) { + sprintf(g->Message, MSG(TABLE_NO_INDEX), name); + rc = RC_NF; + } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO) + rc = RC_OK; // No index + + return rc; + } // end of PlgMakeIndex + +/* -------------------------- Class INDEXDEF ------------------------- */ + +/***********************************************************************/ +/* INDEXDEF Constructor. */ +/***********************************************************************/ +INDEXDEF::INDEXDEF(char *name, bool uniq, int n) + { +//To_Def = NULL; + Next = NULL; + ToKeyParts = NULL; + Name = name; + Unique = uniq; + Invalid = false; + AutoInc = false; + Nparts = 0; + ID = n; +//Offset = 0; +//Offhigh = 0; +//Size = 0; + MaxSame = 1; + } // end of INDEXDEF constructor + +/***********************************************************************/ +/* Set the max same values for each colum after making the index. */ +/***********************************************************************/ +void INDEXDEF::SetMxsame(PXINDEX x) + { + PKPDEF kdp; + PXCOL xcp; + + for (kdp = ToKeyParts, xcp = x->To_KeyCol; + kdp && xcp; kdp = kdp->Next, xcp = xcp->Next) + kdp->Mxsame = xcp->Mxs; + } // end of SetMxsame + +/* -------------------------- Class KPARTDEF ------------------------- */ + +/***********************************************************************/ +/* KPARTDEF Constructor. */ +/***********************************************************************/ +KPARTDEF::KPARTDEF(PSZ name, int n) + { + Next = NULL; + Name = name; + Mxsame = 0; + Ncol = n; + Klen = 0; + } // end of KPARTDEF constructor + +/* -------------------------- XXBASE Class --------------------------- */ + +/***********************************************************************/ +/* XXBASE public constructor. */ +/***********************************************************************/ +XXBASE::XXBASE(PTDBDOS tbxp, bool b) : CSORT(b), + To_Rec((int*&)Record.Memp) + { + Tbxp = tbxp; + Record = Nmblk; + Cur_K = -1; + Old_K = -1; + Num_K = 0; + Ndif = 0; + Bot = Top = Inf = Sup = 0; + Op = OP_EQ; + To_KeyCol = NULL; + Mul = false; + Val_K = -1; + Nblk = Sblk = 0; + Thresh = 7; + ID = -1; + Nth = 0; + } // end of XXBASE constructor + +/***********************************************************************/ +/* Make file output of XINDEX contents. */ +/***********************************************************************/ +void XXBASE::Print(PGLOBAL g, FILE *f, uint n) + { + char m[64]; + + memset(m, ' ', n); // Make margin string + m[n] = '\0'; + fprintf(f, "%sXINDEX: Tbxp=%p Num=%d\n", m, Tbxp, Num_K); + } // end of Print + +/***********************************************************************/ +/* Make string output of XINDEX contents. */ +/***********************************************************************/ +void XXBASE::Print(PGLOBAL g, char *ps, uint z) + { + *ps = '\0'; + strncat(ps, "Xindex", z); + } // end of Print + +/* -------------------------- XINDEX Class --------------------------- */ + +/***********************************************************************/ +/* XINDEX public constructor. */ +/***********************************************************************/ +XINDEX::XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp, int k) + : XXBASE(tdbp, !xdp->IsUnique()) + { + Xdp = xdp; + ID = xdp->GetID(); + Tdbp = tdbp; + X = pxp; + To_LastCol = NULL; + To_LastVal = NULL; + To_Cols = cp; + To_Vals = xp; + Mul = !xdp->IsUnique(); + Srtd = false; + Nk = xdp->GetNparts(); + Nval = (k) ? k : Nk; + Incr = 0; +//Defoff = xdp->GetOffset(); +//Defhigh = xdp->GetOffhigh(); +//Size = xdp->GetSize(); + MaxSame = xdp->GetMaxSame(); + } // end of XINDEX constructor + +/***********************************************************************/ +/* XINDEX Reset: re-initialize a Xindex block. */ +/***********************************************************************/ +void XINDEX::Reset(void) + { + for (PXCOL kp = To_KeyCol; kp; kp = kp->Next) + kp->Val_K = kp->Ndf; + + Cur_K = Num_K; + Old_K = -1; // Needed to avoid not setting CurBlk for Update + Op = (Op == OP_FIRST || Op == OP_NEXT) ? OP_FIRST : + (Op == OP_FSTDIF || Op == OP_NXTDIF) ? OP_FSTDIF : OP_EQ; + Nth = 0; + } // end of Reset + +/***********************************************************************/ +/* XINDEX Close: terminate index and free all allocated data. */ +/* Do not reset other values that are used at return to make. */ +/***********************************************************************/ +void XINDEX::Close(void) + { + // Close file or view of file + X->Close(); + + // De-allocate data + PlgDBfree(Record); + PlgDBfree(Index); + PlgDBfree(Offset); + + // De-allocate Key data + for (PXCOL kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->FreeData(); + + // Column values cannot be retrieved from key anymore + for (int k = 0; k < Nk; k++) + To_Cols[k]->SetKcol(NULL); + + } // end of Close + +/***********************************************************************/ +/* XINDEX compare routine for C Quick/Insertion sort. */ +/***********************************************************************/ +int XINDEX::Qcompare(int *i1, int *i2) + { + register int k; + register PXCOL kcp; + + for (kcp = To_KeyCol, k = 0; kcp; kcp = kcp->Next) + if ((k = kcp->Compare(*i1, *i2))) + break; + +#ifdef DEBTRACE + num_comp++; +#endif + + return k; + } // end of Qcompare + +/***********************************************************************/ +/* Make: Make and index on key column(s). */ +/***********************************************************************/ +bool XINDEX::Make(PGLOBAL g, PIXDEF sxp) + { + /*********************************************************************/ + /* Table can be accessed through an index. */ + /*********************************************************************/ + int k, rc = RC_OK; + int *bof, i, j, n, ndf, nkey; + PKPDEF kdfp = Xdp->GetToKeyParts(); + bool brc = true; + PCOL colp; + PXCOL kp, prev = NULL, kcp = NULL; + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* Allocate the storage that will contain the keys and the file */ + /* positions corresponding to them. */ + /*********************************************************************/ + if ((n = Tdbp->GetMaxSize(g)) < 0) + return true; + else if (!n) { + Num_K = Ndif = 0; + MaxSame = 1; + + // The if condition was suppressed because this may be an existing + // index that is now void because all table lines were deleted. +// if (sxp) + goto nox; // Truncate eventually existing index file +// else +// return false; + + } // endif n + + // File position must be stored + Record.Size = n * sizeof(int); + + if (!PlgDBalloc(g, NULL, Record)) { + sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", n); + goto err; // Error + } // endif + + /*********************************************************************/ + /* Allocate the KXYCOL blocks used to store column values. */ + /*********************************************************************/ + for (k = 0; k < Nk; k++) { + colp = To_Cols[k]; + + if (!kdfp) { + sprintf(g->Message, MSG(INT_COL_ERROR), + (colp) ? colp->GetName() : "???"); + goto err; // Error + } // endif kdfp + + kcp = new(g) KXYCOL(this); + + if (kcp->Init(g, colp, n, true, kdfp->Klen)) + goto err; // Error + + if (prev) { + kcp->Previous = prev; + prev->Next = kcp; + } else + To_KeyCol = kcp; + + prev = kcp; + kdfp = kdfp->Next; + } // endfor k + + To_LastCol = prev; + + /*********************************************************************/ + /* Get the starting information for progress. */ + /*********************************************************************/ + dup->Step = (char*)PlugSubAlloc(g, NULL, 128); + sprintf((char*)dup->Step, MSG(BUILD_INDEX), Xdp->GetName(), Tdbp->Name); + dup->ProgMax = Tdbp->GetProgMax(g); + dup->ProgCur = 0; + + /*********************************************************************/ + /* Standard init: read the file and construct the index table. */ + /* Note: reading will be sequential as To_Kindex is not set. */ + /*********************************************************************/ + for (i = nkey = 0; i < n && rc != RC_EF; i++) { +#if defined(THREAD) + if (!dup->Step) { + strcpy(g->Message, MSG(QUERY_CANCELLED)); + longjmp(g->jumper[g->jump_level], 99); + } // endif Step +#endif // THREAD + + /*******************************************************************/ + /* Read a valid record from table file. */ + /*******************************************************************/ + rc = Tdbp->ReadDB(g); + + // Update progress information + dup->ProgCur = Tdbp->GetProgCur(); + + // Check return code and do whatever must be done according to it + switch (rc) { + case RC_OK: + break; + case RC_EF: + goto end_of_file; + case RC_NF: + continue; + default: + sprintf(g->Message, MSG(RC_READING), rc, Tdbp->Name); + goto err; + } // endswitch rc + + /*******************************************************************/ + /* Get and Store the file position of the last read record for */ + /* future direct access. */ + /*******************************************************************/ + To_Rec[nkey] = Tdbp->GetRecpos(); + + /*******************************************************************/ + /* Get the keys and place them in the key blocks. */ + /*******************************************************************/ + for (k = 0, kcp = To_KeyCol; + k < Nk && kcp; + k++, kcp = kcp->Next) { + colp = To_Cols[k]; + colp->Reset(); + + colp->ReadColumn(g); +// if (colp->ReadColumn(g)) +// goto err; + + kcp->SetValue(colp, nkey); + } // endfor k + + nkey++; // A new valid key was found + } // endfor i + + end_of_file: + + // Update progress information + dup->ProgCur = Tdbp->GetProgMax(g); + + /*********************************************************************/ + /* Record the Index size and eventually resize memory allocation. */ + /*********************************************************************/ + if ((Num_K = nkey) < n) { + PlgDBrealloc(g, NULL, Record, Num_K * sizeof(int)); + + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->ReAlloc(g, Num_K); + + } // endif Num_K + + /*********************************************************************/ + /* Sort the index so we can use an optimized Find algorithm. */ + /* Note: for a unique index we use the non conservative sort */ + /* version because normally all index values are different. */ + /* This was set at CSORT class construction. */ + /* For all indexes, an offset array is made so we can check the */ + /* uniqueness of unique indexes. */ + /*********************************************************************/ + Index.Size = Num_K * sizeof(int); + + if (!PlgDBalloc(g, NULL, Index)) { + sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K); + goto err; // Error + } // endif alloc + + Offset.Size = (Num_K + 1) * sizeof(int); + + if (!PlgDBalloc(g, NULL, Offset)) { + sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Num_K + 1); + goto err; // Error + } // endif alloc + + // Call the sort program, it returns the number of distinct values + if ((Ndif = Qsort(g, Num_K)) < 0) + goto err; // Error during sort + + // Check whether the unique index is unique indeed + if (!Mul) + if (Ndif < Num_K) { + strcpy(g->Message, MSG(INDEX_NOT_UNIQ)); + goto err; + } else + PlgDBfree(Offset); // Not used anymore + + // Use the index to physically reorder the xindex + Srtd = Reorder(g); + + if (Ndif < Num_K) { + // Resize the offset array + PlgDBrealloc(g, NULL, Offset, (Ndif + 1) * sizeof(int)); + + // Initial value of MaxSame + MaxSame = Pof[1] - Pof[0]; + + // Resize the Key array by only keeping the distinct values + for (i = 1; i < Ndif; i++) { + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->Move(i, Pof[i]); + + MaxSame = max(MaxSame, Pof[i + 1] - Pof[i]); + } // endfor i + + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->ReAlloc(g, Ndif); + + } else { + Mul = false; // Current index is unique + PlgDBfree(Offset); // Not used anymore + MaxSame = 1; // Reset it when remaking an index + } // endif Ndif + + /*********************************************************************/ + /* Now do the reduction of the index. Indeed a multi-column index */ + /* can be used for only some of the first columns. For instance if */ + /* an index is defined for column A, B, C PlugDB can use it for */ + /* only the column A or the columns A, B. */ + /* What we do here is to reduce the data so column A will contain */ + /* only the sorted distinct values of A, B will contain data such */ + /* as only distinct values of A,B are stored etc. */ + /* This implies that for each column set an offset array is made */ + /* except if the subset originally contains unique values. */ + /*********************************************************************/ + // Update progress information + dup->Step = STEP(REDUCE_INDEX); + + ndf = Ndif; + To_LastCol->Mxs = MaxSame; + + for (kcp = To_LastCol->Previous; kcp; kcp = kcp->Previous) { + if (!(bof = kcp->MakeOffset(g, ndf))) + goto err; + else + *bof = 0; + + for (n = 0, i = j = 1; i < ndf; i++) + for (kp = kcp; kp; kp = kp->Previous) + if (kp->Compare(n, i)) { + // Values are not equal to last ones + bof[j++] = n = i; + break; + } // endif Compare + + if (j < ndf) { + // Sub-index is multiple + bof[j] = ndf; + ndf = j; // New number of distinct values + + // Resize the Key array by only keeping the distinct values + for (kp = kcp; kp; kp = kp->Previous) { + for (i = 1; i < ndf; i++) + kp->Move(i, bof[i]); + + kp->ReAlloc(g, ndf); + } // endif kcp + + // Resize the offset array + kcp->MakeOffset(g, ndf); + + // Calculate the max same value for this column + kcp->Mxs = ColMaxSame(kcp); + } else { + // Current sub-index is unique + kcp->MakeOffset(g, 0); // The offset is not used anymore + kcp->Mxs = 1; // Unique + } // endif j + + } // endfor kcp + + /*********************************************************************/ + /* For sorted columns and fixed record size, file position can be */ + /* calculated, so the Record array can be discarted. */ + /* Note: for Num_K = 1 any non null value is Ok. */ + /*********************************************************************/ + if (Srtd && Tdbp->Ftype != RECFM_VAR) { + Incr = (Num_K > 1) ? To_Rec[1] : Num_K; + PlgDBfree(Record); + } // endif Srtd + + /*********************************************************************/ + /* Check whether a two-tier find algorithm can be implemented. */ + /* It is currently implemented only for single key indexes. */ + /*********************************************************************/ + if (Nk == 1 && ndf >= 65536) { + // Implement a two-tier find algorithm + for (Sblk = 256; (Sblk * Sblk * 4) < ndf; Sblk *= 2) ; + + Nblk = (ndf -1) / Sblk + 1; + + if (To_KeyCol->MakeBlockArray(g, Nblk, Sblk)) + goto err; // Error + + } // endif Num_K + + nox: + /*********************************************************************/ + /* No valid record read yet for secondary file. */ + /*********************************************************************/ + Cur_K = Num_K; + + /*********************************************************************/ + /* Save the index so it has not to be recalculated. */ + /*********************************************************************/ + if (!SaveIndex(g, sxp)) + brc = false; + + err: + // We don't need the index anymore + Close(); + + if (brc) + printf("%s\n", g->Message); + + return brc; + } // end of Make + +/***********************************************************************/ +/* Return the max size of the intermediate column. */ +/***********************************************************************/ +int XINDEX::ColMaxSame(PXCOL kp) + { + int *kof, i, ck1, ck2, ckn = 1; + PXCOL kcp; + + // Calculate the max same value for this column + for (i = 0; i < kp->Ndf; i++) { + ck1 = i; + ck2 = i + 1; + + for (kcp = kp; kcp; kcp = kcp->Next) { + if (!(kof = (kcp->Next) ? kcp->Kof : Pof)) + break; + + ck1 = kof[ck1]; + ck2 = kof[ck2]; + } // endfor kcp + + ckn = max(ckn, ck2 - ck1); + } // endfor i + + return ckn; + } // end of ColMaxSame + +/***********************************************************************/ +/* Reorder: use the sort index to reorder the data in storage so */ +/* it will be physically sorted and sort index can be removed. */ +/***********************************************************************/ +bool XINDEX::Reorder(PGLOBAL g) + { + register int i, j, k, n; + bool sorted = true; + PXCOL kcp; + PDBUSER dup = (PDBUSER)g->Activityp->Aptr; + + if (Num_K > 500000) { + // Update progress information + dup->Step = STEP(REORDER_INDEX); + dup->ProgMax = Num_K; + dup->ProgCur = 0; + } else + dup = NULL; + + if (!Pex) + return Srtd; + + for (i = 0; i < Num_K; i++) { + if (Pex[i] == Num_K) { // Already moved + continue; + } else if (Pex[i] == i) { // Already placed + if (dup) + dup->ProgCur++; + + continue; + } // endif's Pex + + sorted = false; + + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->Save(i); + + n = To_Rec[i]; + + for (j = i;; j = k) { + k = Pex[j]; + Pex[j] = Num_K; // Mark position as set + + if (k == i) { + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->Restore(j); + + To_Rec[j] = n; + break; // end of loop + } else { + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->Move(j, k); // Move k to j + + To_Rec[j] = To_Rec[k]; + } // endif k + + if (dup) + dup->ProgCur++; + + } // endfor j + + } // endfor i + + // The index is not used anymore + PlgDBfree(Index); + return sorted; + } // end of Reorder + +/***********************************************************************/ +/* Save the index values for this table. */ +/* The problem here is to avoid name duplication, because more than */ +/* one data file can have the same name (but different types) and/or */ +/* the same data file can be used with different block sizes. This is */ +/* why we use Ofn that defaults to the file name but can be set to a */ +/* different name if necessary. */ +/***********************************************************************/ +bool XINDEX::SaveIndex(PGLOBAL g, PIXDEF sxp) + { + char *ftype; + char fn[_MAX_PATH]; + int n[NZ], nof = (Mul) ? (Ndif + 1) : 0; + int id = -1, size = 0; + bool sep, rc = false; + PXCOL kcp = To_KeyCol; + PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; + PDBUSER dup = PlgGetUser(g); + + dup->Step = STEP(SAVING_INDEX); + dup->ProgMax = 15 + 16 * Nk; + dup->ProgCur = 0; + + switch (Tdbp->Ftype) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); + return true; + } // endswitch Ftype + + if ((sep = dup->Catalog->GetBoolCatInfo("SepIndex", false))) { + // Index is saved in a separate file +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + _splitpath(defp->GetOfn(), drive, direc, fname, NULL); + strcat(strcat(fname, "_"), Xdp->GetName()); + _makepath(fn, drive, direc, fname, ftype); + sxp = NULL; + } else { + id = ID; + strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); + } // endif sep + + PlugSetPath(fn, fn, Tdbp->GetPath()); + + if (X->Open(g, fn, id, (sxp) ? MODE_INSERT : MODE_WRITE)) { + printf("%s\n", g->Message); + return true; + } // endif Open + + if (!Ndif) + goto end; // Void index + + /*********************************************************************/ + /* Write the index values on the index file. */ + /*********************************************************************/ + n[0] = ID; // To check validity + n[1] = Nk; // The number of indexed columns + n[2] = nof; // The offset array size or 0 + n[3] = Num_K; // The index size + n[4] = Incr; // Increment of record positions + n[5] = Nblk; n[6] = Sblk; + +#if defined(TRACE) + printf("Saving index %s\n", Xdp->GetName()); + printf("ID=%d Nk=%d nof=%d Num_K=%d Incr=%d Nblk=%d Sblk=%d\n", + ID, Nk, nof, Num_K, Incr, Nblk, Sblk); +#endif // TRACE + + size = X->Write(g, n, NZ, sizeof(int), rc); + dup->ProgCur = 1; + + if (Mul) // Write the offset array + size += X->Write(g, Pof, nof, sizeof(int), rc); + + dup->ProgCur = 5; + + if (!Incr) // Write the record position array(s) + size += X->Write(g, To_Rec, Num_K, sizeof(int), rc); + + dup->ProgCur = 15; + + for (; kcp; kcp = kcp->Next) { + n[0] = kcp->Ndf; // Number of distinct sub-values + n[1] = (kcp->Kof) ? kcp->Ndf + 1 : 0; // 0 if unique + n[2] = (kcp == To_KeyCol) ? Nblk : 0; + n[3] = kcp->Klen; // To be checked later + n[4] = kcp->Type; // To be checked later + + size += X->Write(g, n, NW, sizeof(int), rc); + dup->ProgCur += 1; + + if (n[2]) + size += X->Write(g, kcp->To_Bkeys, Nblk, kcp->Klen, rc); + + dup->ProgCur += 5; + + size += X->Write(g, kcp->To_Keys, n[0], kcp->Klen, rc); + dup->ProgCur += 5; + + if (n[1]) + size += X->Write(g, kcp->Kof, n[1], sizeof(int), rc); + + dup->ProgCur += 5; + } // endfor kcp + +#if defined(TRACE) + printf("Index %s saved, Size=%d\n", Xdp->GetName(), Size); +#endif // TRACE + + end: + X->Close(fn, id); + return rc; + } // end of SaveIndex + +#if !defined(XMAP) +/***********************************************************************/ +/* Init: Open and Initialize a Key Index. */ +/***********************************************************************/ +bool XINDEX::Init(PGLOBAL g) + { + /*********************************************************************/ + /* Table will be accessed through an index table. */ + /* If sorting is required, this will be done later. */ + /*********************************************************************/ + char *ftype; + char fn[_MAX_PATH]; + int k, n, nv[NZ], id = -1; + bool estim = false; + PCOL colp; + PXCOL prev = NULL, kcp = NULL; + PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; + + /*********************************************************************/ + /* Get the estimated table size. */ + /* Note: for fixed tables we must use cardinality to avoid the call */ + /* to MaxBlkSize that could reduce the cardinality value. */ + /*********************************************************************/ + if (Tdbp->Cardinality(NULL)) { + // For DBF tables, Cardinality includes bad or soft deleted lines + // that are not included in the index, and can be larger then the + // index size. + estim = (Tdbp->Ftype == RECFM_DBF); + n = Tdbp->Cardinality(g); // n is exact table size + } else { + // Variable table not optimized + estim = true; // n is an estimate of the size + n = Tdbp->GetMaxSize(g); + } // endif Cardinality + + if (n <= 0) + return !(n == 0); // n < 0 error, n = 0 void table + + /*********************************************************************/ + /* Get the first key column. */ + /*********************************************************************/ + if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) { + strcpy(g->Message, MSG(NO_KEY_COL)); + return true; // Error + } else + colp = To_Cols[0]; + + switch (Tdbp->Ftype) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); + return true; + } // endswitch Ftype + + if (defp->SepIndex()) { + // Index was saved in a separate file +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + _splitpath(defp->GetOfn(), drive, direc, fname, NULL); + strcat(strcat(fname, "_"), Xdp->GetName()); + _makepath(fn, drive, direc, fname, ftype); + } else { + id = ID; + strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); + } // endif sep + + PlugSetPath(fn, fn, Tdbp->GetPath()); + +#if defined(TRACE) + printf("Index %s file: %s\n", Xdp->GetName(), fn); +#endif // TRACE + + /*********************************************************************/ + /* Open the index file and check its validity. */ + /*********************************************************************/ + if (X->Open(g, fn, id, MODE_READ)) + goto err; // No saved values + + // Now start the reading process. + if (X->Read(g, nv, NZ, sizeof(int))) + goto err; + +#if defined(TRACE) + printf("nv=%d %d %d %d %d %d %d\n", + nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]); +#endif // TRACE + + // The test on ID was suppressed because MariaDB can change an index ID + // when other indexes are added or deleted + if (/*nv[0] != ID ||*/ nv[1] != Nk) { + sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); +#if defined(TRACE) + printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk); +#endif // TRACE + goto err; + } // endif + + if (nv[2]) { + Mul = true; + Ndif = nv[2]; + + // Allocate the storage that will contain the offset array + Offset.Size = Ndif * sizeof(int); + + if (!PlgDBalloc(g, NULL, Offset)) { + sprintf(g->Message, MSG(MEM_ALLOC_ERR), "offset", Ndif); + goto err; + } // endif + + if (X->Read(g, Pof, Ndif, sizeof(int))) + goto err; + + Ndif--; // nv[2] is offset size, equal to Ndif + 1 + } else { + Mul = false; + Ndif = nv[3]; + } // endif nv[2] + + if (nv[3] < n && estim) + n = nv[3]; // n was just an evaluated max value + + if (nv[3] != n) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); + goto err; + } // endif + + Num_K = nv[3]; + Incr = nv[4]; + Nblk = nv[5]; + Sblk = nv[6]; + + if (!Incr) { + /*******************************************************************/ + /* Allocate the storage that will contain the file positions. */ + /*******************************************************************/ + Record.Size = Num_K * sizeof(int); + + if (!PlgDBalloc(g, NULL, Record)) { + sprintf(g->Message, MSG(MEM_ALLOC_ERR), "index", Num_K); + goto err; + } // endif + + if (X->Read(g, To_Rec, Num_K, sizeof(int))) + goto err; + + } else + Srtd = true; // Sorted positions can be calculated + + /*********************************************************************/ + /* Allocate the KXYCOL blocks used to store column values. */ + /*********************************************************************/ + for (k = 0; k < Nk; k++) { + if (k == Nval) + To_LastVal = prev; + + if (X->Read(g, nv, NW, sizeof(int))) + goto err; + + colp = To_Cols[k]; + + if (nv[4] != colp->GetResultType() || !colp->GetValue() || + (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { + sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); + goto err; // Error + } // endif GetKey + + kcp = new(g) KXYCOL(this); + + if (kcp->Init(g, colp, nv[0], true, (int)nv[3])) + goto err; // Error + + /*******************************************************************/ + /* Read the index values from the index file. */ + /*******************************************************************/ + if (k == 0 && Nblk) { + if (kcp->MakeBlockArray(g, Nblk, 0)) + goto err; + + // Read block values + if (X->Read(g, kcp->To_Bkeys, Nblk, kcp->Klen)) + goto err; + + } // endif Nblk + + // Read the entire (small) index + if (X->Read(g, kcp->To_Keys, nv[0], kcp->Klen)) + goto err; + + if (nv[1]) { + if (!kcp->MakeOffset(g, nv[1] - 1)) + goto err; + + // Read the offset array + if (X->Read(g, kcp->Kof, nv[1], sizeof(int))) + goto err; + + } // endif n[1] + + if (!kcp->Prefix) + // Indicate that the key column value can be found from KXYCOL + colp->SetKcol(kcp); + + if (prev) { + kcp->Previous = prev; + prev->Next = kcp; + } else + To_KeyCol = kcp; + + prev = kcp; + } // endfor k + + To_LastCol = prev; + + if (Mul && prev) { + // Last key offset is the index offset + kcp->Koff = Offset; + kcp->Koff.Sub = true; + } // endif Mul + + X->Close(); + + /*********************************************************************/ + /* No valid record read yet for secondary file. */ + /*********************************************************************/ + Cur_K = Num_K; + return false; + +err: + Close(); + return true; + } // end of Init + +#else // XMAP +/***********************************************************************/ +/* Init: Open and Initialize a Key Index. */ +/***********************************************************************/ +bool XINDEX::Init(PGLOBAL g) + { + /*********************************************************************/ + /* Table will be accessed through an index table. */ + /* If sorting is required, this will be done later. */ + /*********************************************************************/ + const char *ftype; + BYTE *mbase; + char fn[_MAX_PATH]; + int *nv, k, n, id = -1; + bool estim; + PCOL colp; + PXCOL prev = NULL, kcp = NULL; + PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; + PDBUSER dup = PlgGetUser(g); + + /*********************************************************************/ + /* Get the estimated table size. */ + /* Note: for fixed tables we must use cardinality to avoid the call */ + /* to MaxBlkSize that could reduce the cardinality value. */ + /*********************************************************************/ + if (Tdbp->Cardinality(NULL)) { + // For DBF tables, Cardinality includes bad or soft deleted lines + // that are not included in the index, and can be larger then the + // index size. + estim = (Tdbp->Ftype == RECFM_DBF); + n = Tdbp->Cardinality(g); // n is exact table size + } else { + // Variable table not optimized + estim = true; // n is an estimate of the size + n = Tdbp->GetMaxSize(g); + } // endif Cardinality + + if (n <= 0) + return !(n == 0); // n < 0 error, n = 0 void table + + /*********************************************************************/ + /* Get the first key column. */ + /*********************************************************************/ + if (!Nk || !To_Cols || (!To_Vals && Op != OP_FIRST && Op != OP_FSTDIF)) { + strcpy(g->Message, MSG(NO_KEY_COL)); + return true; // Error + } else + colp = To_Cols[0]; + + switch (Tdbp->Ftype) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); + return true; + } // endswitch Ftype + + if (defp->SepIndex()) { + // Index was save in a separate file +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + _splitpath(defp->GetOfn(), drive, direc, fname, NULL); + strcat(strcat(fname, "_"), Xdp->GetName()); + _makepath(fn, drive, direc, fname, ftype); + } else { + id = ID; + strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); + } // endif SepIndex + + PlugSetPath(fn, fn, Tdbp->GetPath()); + +#if defined(TRACE) + printf("Index %s file: %s\n", Xdp->GetName(), fn); +#endif // TRACE + + /*********************************************************************/ + /* Get a view on the part of the index file containing this index. */ + /*********************************************************************/ + if (!(mbase = (BYTE*)X->FileView(g, fn))) + goto err; + + if (id >= 0) { + // Get offset from the header + IOFF *noff = (IOFF*)mbase; + + // Position the memory base at the offset of this index + mbase += noff[id].Low; + } // endif id + + // Now start the mapping process. + nv = (int*)mbase; + mbase += NZ * sizeof(int); + +#if defined(TRACE) + printf("nv=%d %d %d %d %d %d %d\n", + nv[0], nv[1], nv[2], nv[3], nv[4], nv[5], nv[6]); +#endif // TRACE + + // The test on ID was suppressed because MariaDB can change an index ID + // when other indexes are added or deleted + if (/*nv[0] != ID ||*/ nv[1] != Nk) { + // Not this index + sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); +#if defined(TRACE) + printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk); +#endif // TRACE + goto err; + } // endif nv + + if (nv[2]) { + // Set the offset array memory block + Offset.Memp = mbase; + Offset.Size = nv[2] * sizeof(int); + Offset.Sub = true; + Mul = true; + Ndif = nv[2] - 1; + mbase += Offset.Size; + } else { + Mul = false; + Ndif = nv[3]; + } // endif nv[2] + + if (nv[3] < n && estim) + n = nv[3]; // n was just an evaluated max value + + if (nv[3] != n) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); + goto err; + } // endif + + Num_K = nv[3]; + Incr = nv[4]; + Nblk = nv[5]; + Sblk = nv[6]; + + if (!Incr) { + /*******************************************************************/ + /* Point to the storage that contains the file positions. */ + /*******************************************************************/ + Record.Size = Num_K * sizeof(int); + Record.Memp = mbase; + Record.Sub = true; + mbase += Record.Size; + } else + Srtd = true; // Sorted positions can be calculated + + /*********************************************************************/ + /* Allocate the KXYCOL blocks used to store column values. */ + /*********************************************************************/ + for (k = 0; k < Nk; k++) { + if (k == Nval) + To_LastVal = prev; + + nv = (int*)mbase; + mbase += (NW * sizeof(int)); + + colp = To_Cols[k]; + + if (nv[4] != colp->GetResultType() || !colp->GetValue() || + (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { + sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); + goto err; // Error + } // endif GetKey + + kcp = new(g) KXYCOL(this); + + if (!(mbase = kcp->MapInit(g, colp, nv, mbase))) + goto err; + + if (!kcp->Prefix) + // Indicate that the key column value can be found from KXYCOL + colp->SetKcol(kcp); + + if (prev) { + kcp->Previous = prev; + prev->Next = kcp; + } else + To_KeyCol = kcp; + + prev = kcp; + } // endfor k + + To_LastCol = prev; + + if (Mul && prev) + // Last key offset is the index offset + kcp->Koff = Offset; + + /*********************************************************************/ + /* No valid record read yet for secondary file. */ + /*********************************************************************/ + Cur_K = Num_K; + return false; + +err: + Close(); + return true; + } // end of Init +#endif // XMAP + +/***********************************************************************/ +/* Get Ndif and Num_K from the index file. */ +/***********************************************************************/ +bool XINDEX::GetAllSizes(PGLOBAL g, int &ndif, int &numk) + { + char *ftype; + char fn[_MAX_PATH]; + int n, nv[NZ], id = -1; + bool estim = false; + PDOSDEF defp = (PDOSDEF)Tdbp->To_Def; + + ndif = numk = 0; + + /*********************************************************************/ + /* Get the estimated table size. */ + /* Note: for fixed tables we must use cardinality to avoid the call */ + /* to MaxBlkSize that could reduce the cardinality value. */ + /*********************************************************************/ + if (Tdbp->Cardinality(NULL)) { + // For DBF tables, Cardinality includes bad or soft deleted lines + // that are not included in the index, and can be larger then the + // index size. + estim = (Tdbp->Ftype == RECFM_DBF); + n = Tdbp->Cardinality(g); // n is exact table size + } else { + // Variable table not optimized + estim = true; // n is an estimate of the size + n = Tdbp->GetMaxSize(g); + } // endif Cardinality + + if (n <= 0) + return !(n == 0); // n < 0 error, n = 0 void table + + /*********************************************************************/ + /* Check the key part number. */ + /*********************************************************************/ + if (!Nk) { + strcpy(g->Message, MSG(NO_KEY_COL)); + return true; // Error + } // endif Nk + + switch (Tdbp->Ftype) { + case RECFM_VAR: ftype = ".dnx"; break; + case RECFM_FIX: ftype = ".fnx"; break; + case RECFM_BIN: ftype = ".bnx"; break; + case RECFM_VCT: ftype = ".vnx"; break; + case RECFM_DBF: ftype = ".dbx"; break; + default: + sprintf(g->Message, MSG(INVALID_FTYPE), Tdbp->Ftype); + return true; + } // endswitch Ftype + + if (defp->SepIndex()) { + // Index was saved in a separate file +#if !defined(UNIX) + char drive[_MAX_DRIVE]; +#else + char *drive = NULL; +#endif + char direc[_MAX_DIR]; + char fname[_MAX_FNAME]; + + _splitpath(defp->GetOfn(), drive, direc, fname, NULL); + strcat(strcat(fname, "_"), Xdp->GetName()); + _makepath(fn, drive, direc, fname, ftype); + } else { + id = ID; + strcat(PlugRemoveType(fn, strcpy(fn, defp->GetOfn())), ftype); + } // endif sep + + PlugSetPath(fn, fn, Tdbp->GetPath()); + +#if defined(TRACE) + printf("Index %s file: %s\n", Xdp->GetName(), fn); +#endif // TRACE + + /*********************************************************************/ + /* Open the index file and check its validity. */ + /*********************************************************************/ + if (X->Open(g, fn, id, MODE_READ)) + goto err; // No saved values + + // Get offset from XDB file +//if (X->Seek(g, Defoff, Defhigh, SEEK_SET)) +// goto err; + + // Now start the reading process. + if (X->Read(g, nv, NZ, sizeof(int))) + goto err; + +#if defined(TRACE) + printf("nv=%d %d %d %d\n", nv[0], nv[1], nv[2], nv[3]); +#endif // TRACE + + // The test on ID was suppressed because MariaDB can change an index ID + // when other indexes are added or deleted + if (/*nv[0] != ID ||*/ nv[1] != Nk) { + sprintf(g->Message, MSG(BAD_INDEX_FILE), fn); +#if defined(TRACE) + printf("nv[0]=%d ID=%d nv[1]=%d Nk=%d\n", nv[0], ID, nv[1], Nk); +#endif // TRACE + goto err; + } // endif + + if (nv[2]) { + Mul = true; + Ndif = nv[2] - 1; // nv[2] is offset size, equal to Ndif + 1 + } else { + Mul = false; + Ndif = nv[3]; + } // endif nv[2] + + if (nv[3] < n && estim) + n = nv[3]; // n was just an evaluated max value + + if (nv[3] != n) { + sprintf(g->Message, MSG(OPT_NOT_MATCH), fn); + goto err; + } // endif + + Num_K = nv[3]; + + if (Nk > 1) { + if (nv[2] && X->Seek(g, nv[2] * sizeof(int), 0, SEEK_CUR)) + goto err; + + if (!nv[4] && X->Seek(g, Num_K * sizeof(int), 0, SEEK_CUR)) + goto err; + + if (X->Read(g, nv, NW, sizeof(int))) + goto err; + + PCOL colp = *To_Cols; + + if (nv[4] != colp->GetResultType() || + (nv[3] != colp->GetValue()->GetClen() && nv[4] != TYPE_STRING)) { + sprintf(g->Message, MSG(XCOL_MISMATCH), colp->GetName()); + goto err; // Error + } // endif GetKey + + Ndif = nv[0]; + } // endif Nk + + /*********************************************************************/ + /* Set size values. */ + /*********************************************************************/ + ndif = Ndif; + numk = Num_K; + return false; + +err: + X->Close(); + return true; + } // end of GetAllSizes + +/***********************************************************************/ +/* RANGE: Tell how many records exist for a given value, for an array */ +/* of values, or in a given value range. */ +/***********************************************************************/ +int XINDEX::Range(PGLOBAL g, int limit, bool incl) + { + int i, k, n = 0; + PXOB *xp = To_Vals; + PXCOL kp = To_KeyCol; + OPVAL op = Op; + + switch (limit) { + case 1: Op = (incl) ? OP_GE : OP_GT; break; + case 2: Op = (incl) ? OP_GT : OP_GE; break; + default: return 0; + } // endswitch limit + + /*********************************************************************/ + /* Currently only range of constant values with an EQ operator is */ + /* implemented. Find the number of rows for each given values. */ + /*********************************************************************/ + if (xp[0]->GetType() == TYPE_CONST) { + for (i = 0; kp; kp = kp->Next) { + kp->Valp->SetValue_pval(xp[i]->GetValue(), !kp->Prefix); + if (++i == Nval) break; + } // endfor kp + + if ((k = FastFind(Nval)) < Num_K) + n = k; +// if (limit) +// n = (Mul) ? k : kp->Val_K; +// else +// n = (Mul) ? Pof[kp->Val_K + 1] - k : 1; + + } else { + strcpy(g->Message, MSG(RANGE_NO_JOIN)); + n = -1; // Logical error + } // endif'f Type + + Op = op; + return n; + } // end of Range + +/***********************************************************************/ +/* Return the size of the group (equal values) of the current value. */ +/***********************************************************************/ +int XINDEX::GroupSize(void) + { +#if defined(_DEBUG) + assert(To_LastCol->Val_K >= 0 && To_LastCol->Val_K < Ndif); +#endif // _DEBUG + + if (Nval == Nk) + return (Pof) ? Pof[To_LastCol->Val_K + 1] - Pof[To_LastCol->Val_K] + : 1; + +#if defined(_DEBUG) + assert(To_LastVal); +#endif // _DEBUG + + // Index whose only some columns are used + int ck1, ck2; + + ck1 = To_LastVal->Val_K; + ck2 = ck1 + 1; + +#if defined(_DEBUG) + assert(ck1 >= 0 && ck1 < To_LastVal->Ndf); +#endif // _DEBUG + + for (PXCOL kcp = To_LastVal; kcp; kcp = kcp->Next) { + ck1 = (kcp->Kof) ? kcp->Kof[ck1] : ck1; + ck2 = (kcp->Kof) ? kcp->Kof[ck2] : ck2; + } // endfor kcp + + return ck2 - ck1; + } // end of GroupSize + +/***********************************************************************/ +/* Find Cur_K and Val_K's of the next distinct value of the index. */ +/* Returns false if Ok, true if there are no more different values. */ +/***********************************************************************/ +bool XINDEX::NextValDif(void) + { + int curk; + PXCOL kcp = (To_LastVal) ? To_LastVal : To_LastCol; + + if (++kcp->Val_K < kcp->Ndf) { + Cur_K = curk = kcp->Val_K; + + // (Cur_K return is currently not used by SQLGBX) + for (PXCOL kp = kcp; kp; kp = kp->Next) + Cur_K = (kp->Kof) ? kp->Kof[Cur_K] : Cur_K; + + } else + return true; + + for (kcp = kcp->Previous; kcp; kcp = kcp->Previous) { + if (kcp->Kof && curk < kcp->Kof[kcp->Val_K + 1]) + break; // all previous columns have same value + + curk = ++kcp->Val_K; // This is a break, get new column value + } // endfor kcp + + return false; + } // end of NextValDif + +/***********************************************************************/ +/* XINDEX: Find Cur_K and Val_K's of next index entry. */ +/* If eq is true next values must be equal to last ones up to Nval. */ +/* Returns false if Ok, true if there are no more (equal) values. */ +/***********************************************************************/ +bool XINDEX::NextVal(bool eq) + { + int n, neq = Nk + 1, curk; + PXCOL kcp; + + if (Cur_K == Num_K) + return true; + else + curk = ++Cur_K; + + for (n = Nk, kcp = To_LastCol; kcp; n--, kcp = kcp->Previous) { + if (kcp->Kof) { + if (curk == kcp->Kof[kcp->Val_K + 1]) + neq = n; + + } else { +#ifdef _DEBUG + assert(curk == kcp->Val_K + 1); +#endif // _DEBUG + neq = n; + } // endif Kof + +#ifdef _DEBUG + assert(kcp->Val_K < kcp->Ndf); +#endif // _DEBUG + + // If this is not a break... + if (neq > n) + break; // all previous columns have same value + + curk = ++kcp->Val_K; // This is a break, get new column value + } // endfor kcp + + // Return true if no more values or, in case of "equal" values, + // if the last used column value has changed + return (Cur_K == Num_K || (eq && neq <= Nval)); + } // end of NextVal + +/***********************************************************************/ +/* XINDEX: Fetch a physical or logical record. */ +/***********************************************************************/ +int XINDEX::Fetch(PGLOBAL g) + { + int n; + PXCOL kp; + + if (Num_K == 0) + return -1; // means end of file + + /*********************************************************************/ + /* Table read through a sorted index. */ + /*********************************************************************/ + switch (Op) { + case OP_NEXT: // Read next + if (NextVal(false)) + return -1; // End of indexed file + + break; + case OP_FIRST: // Read first + for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next) + kp->Val_K = 0; + + Op = OP_NEXT; + break; + case OP_SAME: // Read next same + // Logically the key values should be the same as before +#if defined(TRACE) + printf("looking for next same value\n"); +#endif // TRACE + + if (NextVal(true)) { + Op = OP_EQ; + return -2; // no more equal values + } // endif NextVal + + break; + case OP_NXTDIF: // Read next dif +// while (!NextVal(true)) ; + +// if (Cur_K >= Num_K) +// return -1; // End of indexed file + if (NextValDif()) + return -1; // End of indexed file + + break; + case OP_FSTDIF: // Read first diff + for (Cur_K = 0, kp = To_KeyCol; kp; kp = kp->Next) + kp->Val_K = 0; + + Op = (Mul || Nval < Nk) ? OP_NXTDIF : OP_NEXT; + break; + default: // Should be OP_EQ +// if (Tbxp->Key_Rank < 0) { + /***************************************************************/ + /* Look for the first key equal to the link column values */ + /* and return its rank whithin the index table. */ + /***************************************************************/ + for (n = 0, kp = To_KeyCol; n < Nval && kp; n++, kp = kp->Next) + if (kp->InitFind(g, To_Vals[n])) + return -1; // No more constant values + + Nth++; + +#if defined(TRACE) + printf("Fetch: Looking for new value\n"); +#endif // TRACE + Cur_K = FastFind(Nval); + + if (Cur_K >= Num_K) + /*************************************************************/ + /* Rank not whithin index table, signal record not found. */ + /*************************************************************/ + return -2; + + else if (Mul || Nval < Nk) + Op = OP_SAME; + + } // endswitch Op + + /*********************************************************************/ + /* If rank is equal to stored rank, record is already there. */ + /*********************************************************************/ + if (Cur_K == Old_K) + return -3; // Means record already there + else + Old_K = Cur_K; // Store rank of newly read record + + /*********************************************************************/ + /* Return the position of the required record. */ + /*********************************************************************/ + return (Incr) ? Cur_K * Incr : To_Rec[Cur_K]; + } // end of Fetch + +/***********************************************************************/ +/* FastFind: Returns the index of matching record in a join using an */ +/* optimized algorithm based on dichotomie and optimized comparing. */ +/***********************************************************************/ +int XINDEX::FastFind(int nv) + { + register int curk, sup, inf, i= 0, k, n = 2; + register PXCOL kp, kcp; + + assert((int)nv == Nval); + + if (Nblk && Op == OP_EQ) { + // Look in block values to find in which block to search + sup = Nblk; + inf = -1; + + while (n && sup - inf > 1) { + i = (inf + sup) >> 1; + + n = To_KeyCol->CompBval(i); + + if (n < 0) + sup = i; + else + inf = i; + + } // endwhile + + if (inf < 0) + return Num_K; + +// i = inf; + inf *= Sblk; + + if ((sup = inf + Sblk) > To_KeyCol->Ndf) + sup = To_KeyCol->Ndf; + + inf--; + } else { + inf = -1; + sup = To_KeyCol->Ndf; + } // endif Nblk + + for (k = 0, kcp = To_KeyCol; kcp; kcp = kcp->Next) { + while (sup - inf > 1) { + i = (inf + sup) >> 1; + + n = kcp->CompVal(i); + + if (n < 0) + sup = i; + else if (n > 0) + inf = i; + else + break; + + } // endwhile + + if (n) { + if (Op != OP_EQ) { + // Currently only OP_GT or OP_GE + kcp->Val_K = curk = sup; + + // Check for value changes in previous key parts + for (kp = kcp->Previous; kp; kp = kp->Previous) + if (kp->Kof && curk < kp->Kof[kp->Val_K + 1]) + break; + else + curk = ++kp->Val_K; + + n = 0; + } // endif Op + + break; + } // endif n + + kcp->Val_K = i; + + if (++k == Nval) { + if (Op == OP_GT) { // n is always 0 + curk = ++kcp->Val_K; // Increment value by 1 + + // Check for value changes in previous key parts + for (kp = kcp->Previous; kp; kp = kp->Previous) + if (kp->Kof && curk < kp->Kof[kp->Val_K + 1]) + break; // Not changed + else + curk = ++kp->Val_K; + + } // endif Op + + break; // So kcp remains pointing the last tested block + } // endif k + + if (kcp->Kof) { + inf = kcp->Kof[i] - 1; + sup = kcp->Kof[i + 1]; + } else { + inf = i - 1; + sup = i + 1; + } // endif Kof + + } // endfor k, kcp + + if (n) { + // Record not found + for (kcp = To_KeyCol; kcp; kcp = kcp->Next) + kcp->Val_K = kcp->Ndf; // Not a valid value + + return Num_K; + } // endif n + + for (curk = kcp->Val_K; kcp; kcp = kcp->Next) { + kcp->Val_K = curk; + curk = (kcp->Kof) ? kcp->Kof[kcp->Val_K] : kcp->Val_K; + } // endfor kcp + + return curk; + } // end of FastFind + +/* -------------------------- XINDXS Class --------------------------- */ + +/***********************************************************************/ +/* XINDXS public constructor. */ +/***********************************************************************/ +XINDXS::XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp) + : XINDEX(tdbp, xdp, pxp, cp, xp) + { + Srtd = To_Cols[0]->GetOpt() < 0; // ????? + } // end of XINDXS constructor + +/***********************************************************************/ +/* XINDXS compare routine for C Quick/Insertion sort. */ +/***********************************************************************/ +int XINDXS::Qcompare(int *i1, int *i2) + { +#ifdef DEBTRACE + num_comp++; +#endif + + return To_KeyCol->Compare(*i1, *i2); + } // end of Qcompare + +/***********************************************************************/ +/* Range: Tell how many records exist for given value(s): */ +/* If limit=0 return range for these values. */ +/* If limit=1 return the start of range. */ +/* If limit=2 return the end of range. */ +/***********************************************************************/ +int XINDXS::Range(PGLOBAL g, int limit, bool incl) + { + int k, n = 0; + PXOB xp = To_Vals[0]; + PXCOL kp = To_KeyCol; + OPVAL op = Op; + + switch (limit) { + case 1: Op = (incl) ? OP_GE : OP_GT; break; + case 2: Op = (incl) ? OP_GT : OP_GE; break; + default: Op = OP_EQ; + } // endswitch limit + + /*********************************************************************/ + /* Currently only range of constant values with an EQ operator is */ + /* implemented. Find the number of rows for each given values. */ + /*********************************************************************/ + if (xp->GetType() == TYPE_CONST) { + kp->Valp->SetValue_pval(xp->GetValue(), !kp->Prefix); + + if ((k = FastFind(Nval)) < Num_K) + if (limit) + n = (Mul) ? k : kp->Val_K; + else + n = (Mul) ? Pof[kp->Val_K + 1] - k : 1; + + } else { + strcpy(g->Message, MSG(RANGE_NO_JOIN)); + n = -1; // Logical error + } // endif'f Type + + Op = op; + return n; + } // end of Range + +/***********************************************************************/ +/* Return the size of the group (equal values) of the current value. */ +/***********************************************************************/ +int XINDXS::GroupSize(void) + { +#if defined(_DEBUG) + assert(To_KeyCol->Val_K >= 0 && To_KeyCol->Val_K < Ndif); +#endif // _DEBUG + return (Pof) ? Pof[To_KeyCol->Val_K + 1] - Pof[To_KeyCol->Val_K] + : 1; + } // end of GroupSize + +/***********************************************************************/ +/* XINDXS: Find Cur_K and Val_K of next index value. */ +/* If b is true next value must be equal to last one. */ +/* Returns false if Ok, true if there are no more (equal) values. */ +/***********************************************************************/ +bool XINDXS::NextVal(bool eq) + { + bool rc; + + if (To_KeyCol->Val_K == Ndif) + return true; + + if (Mul) { + int limit = Pof[To_KeyCol->Val_K + 1]; + +#ifdef _DEBUG + assert(Cur_K < limit); + assert(To_KeyCol->Val_K < Ndif); +#endif // _DEBUG + + if (++Cur_K == limit) { + To_KeyCol->Val_K++; + rc = (eq || limit == Num_K); + } else + rc = false; + + } else + rc = (To_KeyCol->Val_K = ++Cur_K) == Num_K || eq; + + return rc; + } // end of NextVal + +/***********************************************************************/ +/* XINDXS: Fetch a physical or logical record. */ +/***********************************************************************/ +int XINDXS::Fetch(PGLOBAL g) + { + if (Num_K == 0) + return -1; // means end of file + + /*********************************************************************/ + /* Table read through a sorted index. */ + /*********************************************************************/ + switch (Op) { + case OP_NEXT: // Read next + if (NextVal(false)) + return -1; // End of indexed file + + break; + case OP_FIRST: // Read first + To_KeyCol->Val_K = Cur_K = 0; + Op = OP_NEXT; + break; + case OP_SAME: // Read next same +#if defined(TRACE) +// printf("looking for next same value\n"); +#endif // TRACE + + if (!Mul || NextVal(true)) { + Op = OP_EQ; + return -2; // No more equal values + } // endif Mul + + break; + case OP_NXTDIF: // Read next dif + if (++To_KeyCol->Val_K == Ndif) + return -1; // End of indexed file + + Cur_K = Pof[To_KeyCol->Val_K]; + break; + case OP_FSTDIF: // Read first diff + To_KeyCol->Val_K = Cur_K = 0; + Op = (Mul) ? OP_NXTDIF : OP_NEXT; + break; + default: // Should OP_EQ + /*****************************************************************/ + /* Look for the first key equal to the link column values */ + /* and return its rank whithin the index table. */ + /*****************************************************************/ + if (To_KeyCol->InitFind(g, To_Vals[0])) + return -1; // No more constant values + else + Nth++; + +#if defined(TRACE) + printf("Fetch: Looking for new value\n"); +#endif // TRACE + + Cur_K = FastFind(1); + + if (Cur_K >= Num_K) + // Rank not whithin index table, signal record not found + return -2; + else if (Mul) + Op = OP_SAME; + + } // endswitch Op + + /*********************************************************************/ + /* If rank is equal to stored rank, record is already there. */ + /*********************************************************************/ + if (Cur_K == Old_K) + return -3; // Means record already there + else + Old_K = Cur_K; // Store rank of newly read record + + /*********************************************************************/ + /* Return the position of the required record. */ + /*********************************************************************/ + return (Incr) ? Cur_K * Incr : To_Rec[Cur_K]; + } // end of Fetch + +/***********************************************************************/ +/* FastFind: Returns the index of matching indexed record using an */ +/* optimized algorithm based on dichotomie and optimized comparing. */ +/***********************************************************************/ +int XINDXS::FastFind(int nk) + { + register int sup, inf, i= 0, n = 2; + register PXCOL kcp = To_KeyCol; + + if (Nblk && Op == OP_EQ) { + // Look in block values to find in which block to search + sup = Nblk; + inf = -1; + + while (n && sup - inf > 1) { + i = (inf + sup) >> 1; + + n = kcp->CompBval(i); + + if (n < 0) + sup = i; + else + inf = i; + + } // endwhile + + if (inf < 0) + return Num_K; + +// i = inf; + inf *= Sblk; + + if ((sup = inf + Sblk) > Ndif) + sup = Ndif; + + inf--; + } else { + inf = -1; + sup = Ndif; + } // endif Nblk + + while (sup - inf > 1) { + i = (inf + sup) >> 1; + + n = kcp->CompVal(i); + + if (n < 0) + sup = i; + else if (n > 0) + inf = i; + else + break; + + } // endwhile + + if (!n && Op == OP_GT) { + ++i; + } else if (n && Op != OP_EQ) { + // Currently only OP_GT or OP_GE + i = sup; + n = 0; + } // endif sup + + kcp->Val_K = i; // Used by FillValue + return ((n) ? Num_K : (Mul) ? Pof[i] : i); + } // end of FastFind + +/* -------------------------- XLOAD Class --------------------------- */ + +/***********************************************************************/ +/* XLOAD constructor. */ +/***********************************************************************/ +XLOAD::XLOAD(void) + { + Hfile = INVALID_HANDLE_VALUE; +#if defined(WIN32) && defined(XMAP) + ViewBase = NULL; +#endif // WIN32 && XMAP + NewOff.Val = 0LL; +} // end of XLOAD constructor + +/***********************************************************************/ +/* Close the index huge file. */ +/***********************************************************************/ +void XLOAD::Close(void) + { + if (Hfile != INVALID_HANDLE_VALUE) { + CloseFileHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + } // endif Hfile + +#if defined(WIN32) && defined(XMAP) + if (ViewBase) { + if (!UnmapViewOfFile(ViewBase)) + printf("Error %d closing Viewmap\n", GetLastError()); + + ViewBase = NULL; + } // endif ViewBase +#endif // WIN32 && XMAP + + } // end of Close + +/* --------------------------- XFILE Class --------------------------- */ + +/***********************************************************************/ +/* XFILE constructor. */ +/***********************************************************************/ +XFILE::XFILE(void) : XLOAD() + { + Xfile = NULL; +#if defined(XMAP) && !defined(WIN32) + Mmp = NULL; +#endif // XMAP && !WIN32 + } // end of XFILE constructor + +/***********************************************************************/ +/* Xopen function: opens a file using native API's. */ +/***********************************************************************/ +bool XFILE::Open(PGLOBAL g, char *filename, int id, MODE mode) + { + char *pmod; + bool rc; + IOFF noff[MAX_INDX]; + + /*********************************************************************/ + /* Open the index file according to mode. */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: pmod = "rb"; break; + case MODE_WRITE: pmod = "wb"; break; + case MODE_INSERT: pmod = "ab"; break; + default: + sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode); + return true; + } // endswitch mode + + if (!(Xfile= global_fopen(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, pmod))) { +#if defined(TRACE) + printf("Open: %s\n", g->Message); +#endif // TRACE + return true; + } // endif Xfile + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* Position the cursor at end of file so ftell returns file size. */ + /*******************************************************************/ + if (fseek(Xfile, 0, SEEK_END)) { + sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek"); + return true; + } // endif + + NewOff.Low = (int)ftell(Xfile); + } else if (mode == MODE_WRITE) { + if (id >= 0) { + // New not sep index file. Write the header. + memset(noff, 0, sizeof(noff)); + Write(g, noff, sizeof(IOFF), MAX_INDX, rc); + fseek(Xfile, 0, SEEK_END); + NewOff.Low = (int)ftell(Xfile); + } // endif id + + } else if (mode == MODE_READ && id >= 0) { + // Get offset from the header + if (fread(noff, sizeof(IOFF), MAX_INDX, Xfile) != MAX_INDX) { + sprintf(g->Message, MSG(XFILE_READERR), errno); + return true; + } // endif MAX_INDX + + // Position the cursor at the offset of this index + if (fseek(Xfile, noff[id].Low, SEEK_SET)) { + sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek"); + return true; + } // endif + + } // endif mode + + return false; + } // end of Open + +/***********************************************************************/ +/* Move into an index file. */ +/***********************************************************************/ +bool XFILE::Seek(PGLOBAL g, int low, int high, int origin) + { +#if defined(_DEBUG) + assert(high == 0); +#endif // !_DEBUG + + if (fseek(Xfile, low, origin)) { + sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Xseek"); + return true; + } // endif + +//ftell(Xfile); + return false; + } // end of Seek + +/***********************************************************************/ +/* Read from the index file. */ +/***********************************************************************/ +bool XFILE::Read(PGLOBAL g, void *buf, int n, int size) + { + if (fread(buf, size, n, Xfile) != (size_t)n) { + sprintf(g->Message, MSG(XFILE_READERR), errno); + return true; + } // endif size + + return false; + } // end of Read + +/***********************************************************************/ +/* Write on index file, set rc and return the number of bytes written */ +/***********************************************************************/ +int XFILE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc) + { + int niw = (int)fwrite(buf, size, n, Xfile); + + if (niw != n) { + sprintf(g->Message, MSG(XFILE_WRITERR), strerror(errno)); + rc = true; + } // endif size + + return niw * size; + } // end of Write + +/***********************************************************************/ +/* Update the file header and close the index file. */ +/***********************************************************************/ +void XFILE::Close(char *fn, int id) + { + if (id >= 0 && fn && Xfile) { + fclose(Xfile); + + if ((Xfile = fopen(fn, "r+b"))) + if (!fseek(Xfile, id * sizeof(IOFF), SEEK_SET)) + fwrite(&NewOff, sizeof(int), 2, Xfile); + + } // endif id + + Close(); + } // end of Close + +/***********************************************************************/ +/* Close the index file. */ +/***********************************************************************/ +void XFILE::Close(void) + { + XLOAD::Close(); + + if (Xfile) { + fclose(Xfile); + Xfile = NULL; + } // endif Xfile + +#if defined(XMAP) && !defined(WIN32) + if (Mmp) { + CloseMemMap(Mmp->memory, Mmp->lenL); + Mmp = NULL; + } // endif Mmp +#endif // XMAP + } // end of Close + +#if defined(XMAP) + /*********************************************************************/ + /* Map the entire index file. */ + /*********************************************************************/ +void *XFILE::FileView(PGLOBAL g, char *fn) + { + HANDLE h; + + Mmp = (MMP)PlugSubAlloc(g, NULL, sizeof(MEMMAP)); + h = CreateFileMap(g, fn, Mmp, MODE_READ, false); + + if (h == INVALID_HANDLE_VALUE || (!Mmp->lenH && !Mmp->lenL)) { + if (!(*g->Message)) + strcpy(g->Message, MSG(FILE_MAP_ERR)); + + CloseFileHandle(h); // Not used anymore + return NULL; // No saved values + } // endif h + + CloseFileHandle(h); // Not used anymore + return Mmp->memory; + } // end of FileView +#endif // XMAP + +/* -------------------------- XHUGE Class --------------------------- */ + +/***********************************************************************/ +/* Xopen function: opens a file using native API's. */ +/***********************************************************************/ +bool XHUGE::Open(PGLOBAL g, char *filename, int id, MODE mode) + { + IOFF noff[MAX_INDX]; + + if (Hfile != INVALID_HANDLE_VALUE) { + sprintf(g->Message, MSG(FILE_OPEN_YET), filename); + return true; + } // endif + +#if defined(TRACE) + printf( "Xopen: filename=%s mode=%d\n", filename, mode); +#endif // TRACE + +#if defined(WIN32) + LONG high = 0; + DWORD rc, drc, access, share, creation; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + access = GENERIC_READ; + share = FILE_SHARE_READ; + creation = OPEN_EXISTING; + break; + case MODE_WRITE: + access = GENERIC_WRITE; + share = 0; + creation = CREATE_ALWAYS; + break; + case MODE_INSERT: + access = GENERIC_WRITE; + share = 0; + creation = OPEN_EXISTING; + break; + default: + sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode); + return true; + } // endswitch + + Hfile = CreateFile(filename, access, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + sprintf(g->Message, MSG(OPEN_ERROR), rc, mode, filename); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, rc, 0, + (LPTSTR)filename, sizeof(filename), NULL); + strcat(g->Message, filename); + return true; + } // endif Hfile + +#ifdef DEBTRACE + fprintf(debug, + " access=%p share=%p creation=%d handle=%p fn=%s\n", + access, share, creation, Hfile, filename); +#endif + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* In Insert mode we must position the cursor at end of file. */ + /*******************************************************************/ + rc = SetFilePointer(Hfile, 0, &high, FILE_END); + + if (rc == INVALID_SET_FILE_POINTER && (drc = GetLastError()) != NO_ERROR) { + sprintf(g->Message, MSG(ERROR_IN_SFP), drc); + CloseHandle(Hfile); + Hfile = INVALID_HANDLE_VALUE; + return true; + } // endif + + NewOff.Low = (int)rc; + NewOff.High = (int)high; + } else if (mode == MODE_WRITE) { + if (id >= 0) { + // New not sep index file. Write the header. + memset(noff, 0, sizeof(noff)); + rc = WriteFile(Hfile, noff, sizeof(noff), &drc, NULL); + NewOff.Low = (int)drc; + } // endif id + + } else if (mode == MODE_READ && id >= 0) { + // Get offset from the header + rc = ReadFile(Hfile, noff, sizeof(noff), &drc, NULL); + + if (!rc) { + sprintf(g->Message, MSG(XFILE_READERR), GetLastError()); + return true; + } // endif rc + + // Position the cursor at the offset of this index + rc = SetFilePointer(Hfile, noff[id].Low, + (PLONG)&noff[id].High, FILE_BEGIN); + + if (rc == INVALID_SET_FILE_POINTER) { + sprintf(g->Message, MSG(FUNC_ERRNO), GetLastError(), "SetFilePointer"); + return true; + } // endif + + } // endif Mode + +#else // UNIX + int rc = 0; + int oflag = O_LARGEFILE; // Enable file size > 2G + mode_t pmod = 0; + + /*********************************************************************/ + /* Create the file object according to access mode */ + /*********************************************************************/ + switch (mode) { + case MODE_READ: + oflag |= O_RDONLY; + break; + case MODE_WRITE: + oflag |= O_WRONLY | O_CREAT | O_TRUNC; + pmod = S_IREAD | S_IWRITE; + break; + case MODE_INSERT: + oflag |= (O_WRONLY | O_APPEND); + break; + default: + sprintf(g->Message, MSG(BAD_FUNC_MODE), "Xopen", mode); + return true; + } // endswitch + + Hfile= global_open(g, MSGID_OPEN_ERROR_AND_STRERROR, filename, oflag, pmod); + + if (Hfile == INVALID_HANDLE_VALUE) { + rc = errno; +#if defined(TRACE) + printf("Open: %s\n", g->Message); +#endif // TRACE + return true; + } // endif Hfile + +#if defined(TRACE) + printf(" rc=%d oflag=%p mode=%d handle=%d fn=%s\n", + rc, oflag, mode, Hfile, filename); +#endif // TRACE + + if (mode == MODE_INSERT) { + /*******************************************************************/ + /* Position the cursor at end of file so ftell returns file size. */ + /*******************************************************************/ + if (!(NewOff.Val = (longlong)lseek64(Hfile, 0LL, SEEK_END))) { + sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Seek"); + return true; + } // endif + + } else if (mode == MODE_WRITE) { + if (id >= 0) { + // New not sep index file. Write the header. + memset(noff, 0, sizeof(noff)); + NewOff.Low = write(Hfile, &noff, sizeof(noff)); + } // endif id + + } else if (mode == MODE_READ && id >= 0) { + // Get offset from the header + if (read(Hfile, noff, sizeof(noff)) != sizeof(noff)) { + sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno)); + return true; + } // endif MAX_INDX + + // Position the cursor at the offset of this index + if (!lseek64(Hfile, noff[id].Val, SEEK_SET)) { + sprintf(g->Message, MSG(FUNC_ERRNO), errno, "Hseek"); + return true; + } // endif + + } // endif mode +#endif // UNIX + + return false; + } // end of Open + +/***********************************************************************/ +/* Go to position in a huge file. */ +/***********************************************************************/ +bool XHUGE::Seek(PGLOBAL g, int low, int high, int origin) + { +#if defined(WIN32) + LONG hi = high; + DWORD rc = SetFilePointer(Hfile, low, &hi, origin); + + if (rc == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + sprintf(g->Message, MSG(FUNC_ERROR), "Xseek"); + return true; + } // endif + +#else // UNIX + off64_t pos = (off64_t)low + + (off64_t)high * ((off64_t)0x100 * (off64_t)0x1000000); + + if (lseek64(Hfile, pos, origin) < 0) { + sprintf(g->Message, MSG(ERROR_IN_LSK), errno); +#if defined(TRACE) + printf("lseek64 error %d\n", errno); +#endif // TRACE + return true; + } // endif lseek64 + +#if defined(TRACE) + printf("Seek: low=%d high=%d\n", low, high); +#endif // TRACE +#endif // UNIX + + return false; + } // end of Seek + +/***********************************************************************/ +/* Read from a huge index file. */ +/***********************************************************************/ +bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size) + { + bool rc = false; + +#if defined(WIN32) + bool brc; + DWORD nbr, count = (DWORD)(n * size); + + brc = ReadFile(Hfile, buf, count, &nbr, NULL); + + if (brc) { + if (nbr != count) { + strcpy(g->Message, MSG(EOF_INDEX_FILE)); + rc = true; + } // endif nbr + + } else { + char *buf[256]; + DWORD drc = GetLastError(); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)buf, sizeof(buf), NULL); + sprintf(g->Message, MSG(READ_ERROR), "index file", buf); + rc = true; + } // endif brc +#else // UNIX + ssize_t count = (ssize_t)(n * size); + +#if defined(TRACE) + printf("Hfile=%d n=%d size=%d count=%d\n", Hfile, n, size, count); +#endif // TRACE + + if (read(Hfile, buf, count) != count) { + sprintf(g->Message, MSG(READ_ERROR), "Index file", strerror(errno)); +#if defined(TRACE) + printf("read error %d\n", errno); +#endif // TRACE + rc = true; + } // endif nbr +#endif // UNIX + + return rc; + } // end of Read + +/***********************************************************************/ +/* Write on a huge index file. */ +/***********************************************************************/ +int XHUGE::Write(PGLOBAL g, void *buf, int n, int size, bool& rc) + { +#if defined(WIN32) + bool brc; + DWORD nbw, count = (DWORD)n * (DWORD) size; + + brc = WriteFile(Hfile, buf, count, &nbw, NULL); + + if (!brc) { + char msg[256]; + DWORD drc = GetLastError(); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0, + (LPTSTR)msg, sizeof(msg), NULL); + sprintf(g->Message, MSG(WRITING_ERROR), "index file", msg); + rc = true; + } // endif size + + return (int)nbw; +#else // UNIX + ssize_t nbw; + size_t count = (size_t)n * (size_t)size; + + nbw = write(Hfile, buf, count); + + if (nbw != (signed)count) { + sprintf(g->Message, MSG(WRITING_ERROR), + "index file", strerror(errno)); + rc = true; + } // endif nbw + + return (int)nbw; +#endif // UNIX + } // end of Write + +/***********************************************************************/ +/* Update the file header and close the index file. */ +/***********************************************************************/ +void XHUGE::Close(char *fn, int id) + { +#if defined(WIN32) + if (id >= 0 && fn) { + CloseFileHandle(Hfile); + Hfile = CreateFile(fn, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Hfile != INVALID_HANDLE_VALUE) + if (SetFilePointer(Hfile, id * sizeof(IOFF), NULL, FILE_BEGIN) + != INVALID_SET_FILE_POINTER) { + DWORD nbw; + + WriteFile(Hfile, &NewOff, sizeof(IOFF), &nbw, NULL); + } // endif SetFilePointer + + } // endif id +#else // !WIN32 + if (id >= 0 && fn) { + fcntl(Hfile, F_SETFD, O_WRONLY); + + if (lseek(Hfile, id * sizeof(IOFF), SEEK_SET)) + write(Hfile, &NewOff, sizeof(IOFF)); + + } // endif id +#endif // !WIN32 + + XLOAD::Close(); + } // end of Close + +#if defined(XMAP) +/***********************************************************************/ +/* Don't know whether this is possible for huge files. */ +/***********************************************************************/ +void *XHUGE::FileView(PGLOBAL g, char *fn) + { + strcpy(g->Message, MSG(NO_PART_MAP)); + return NULL; + } // end of FileView +#endif // XMAP + +/* -------------------------- XXROW Class --------------------------- */ + +/***********************************************************************/ +/* XXROW Public Constructor. */ +/***********************************************************************/ +XXROW::XXROW(PTDBDOS tdbp) : XXBASE(tdbp, false) + { + Tdbp = tdbp; + Valp = NULL; + } // end of XXROW constructor + +/***********************************************************************/ +/* XXROW Reset: re-initialize a Kindex block. */ +/***********************************************************************/ +void XXROW::Reset(void) + { +#if defined(_DEBUG) + assert(Tdbp->GetLink()); // This a join index +#endif // _DEBUG + } // end of Reset + +/***********************************************************************/ +/* Init: Open and Initialize a Key Index. */ +/***********************************************************************/ +bool XXROW::Init(PGLOBAL g) + { + /*********************************************************************/ + /* Table will be accessed through an index table. */ + /* To_Link should not be NULL. */ + /*********************************************************************/ + if (!Tdbp->GetLink() || Tbxp->GetKnum() != 1) + return true; + + if ((*Tdbp->GetLink())->GetResultType() != TYPE_INT) { + strcpy(g->Message, MSG(TYPE_MISMATCH)); + return true; + } else + Valp = (*Tdbp->GetLink())->GetValue(); + + if ((Num_K = Tbxp->Cardinality(g)) < 0) + return true; // Not a fixed file + + /*********************************************************************/ + /* The entire table is indexed, no need to construct the index. */ + /*********************************************************************/ + Cur_K = Num_K; + return false; + } // end of Init + +/***********************************************************************/ +/* RANGE: Tell how many record exist in a given value range. */ +/***********************************************************************/ +int XXROW::Range(PGLOBAL g, int limit, bool incl) + { + int n = Valp->GetIntValue(); + + switch (limit) { + case 1: n += ((incl) ? 0 : 1); break; + case 2: n += ((incl) ? 1 : 0); break; + default: n = 1; + } // endswitch limit + + return n; + } // end of Range + +/***********************************************************************/ +/* XXROW: Fetch a physical or logical record. */ +/***********************************************************************/ +int XXROW::Fetch(PGLOBAL g) + { + if (Num_K == 0) + return -1; // means end of file + + /*********************************************************************/ + /* Look for a key equal to the link column of previous table, */ + /* and return its rank whithin the index table. */ + /*********************************************************************/ + Cur_K = FastFind(1); + + if (Cur_K >= Num_K) + /*******************************************************************/ + /* Rank not whithin index table, signal record not found. */ + /*******************************************************************/ + return -2; // Means record not found + + /*********************************************************************/ + /* If rank is equal to stored rank, record is already there. */ + /*********************************************************************/ + if (Cur_K == Old_K) + return -3; // Means record already there + else + Old_K = Cur_K; // Store rank of newly read record + + return Cur_K; + } // end of Fetch + +/***********************************************************************/ +/* FastFind: Returns the index of matching record in a join. */ +/***********************************************************************/ +int XXROW::FastFind(int nk) + { + int n = Valp->GetIntValue(); + + if (n < 0) + return (Op == OP_EQ) ? (-1) : 0; + else if (n > Num_K) + return Num_K; + else + return (Op == OP_GT) ? n : (n - 1); + + } // end of FastFind + +/* ------------------------- KXYCOL Classes -------------------------- */ + +/***********************************************************************/ +/* KXYCOL public constructor. */ +/***********************************************************************/ +KXYCOL::KXYCOL(PKXBASE kp) : To_Keys(Keys.Memp), + To_Bkeys(Bkeys.Memp), Kof((CPINT&)Koff.Memp) + { + Next = NULL; + Previous = NULL; + Kxp = kp; + Colp = NULL; + IsSorted = false; + Asc = true; + Keys = Nmblk; + Kblp = NULL; + Bkeys = Nmblk; + Blkp = NULL; + Valp = NULL; + Klen = 0; + Kprec = 0; + Type = TYPE_ERROR; + Prefix = false; + Koff = Nmblk; + Val_K = 0; + Ndf = 0; + Mxs = 0; + } // end of KXYCOL constructor + +/***********************************************************************/ +/* KXYCOL Init: initialize and allocate storage. */ +/* Key length kln can be smaller than column length for CHAR columns. */ +/***********************************************************************/ +bool KXYCOL::Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln) + { + int len = colp->GetLength(), prec = colp->GetPrecision(); + + // Currently no indexing on NULL columns + if (colp->IsNullable()) { + sprintf(g->Message, "Cannot index nullable column %s", colp->GetName()); + return true; + } // endif nullable + + if (kln && len > kln && colp->GetResultType() == TYPE_STRING) { + len = kln; + Prefix = true; + } // endif kln + +#ifdef DEBTRACE + htrc("KCOL(%p) Init: col=%s n=%d type=%d sm=%d\n", + this, colp->GetName(), n, colp->GetResultType(), sm); +#endif + + // Allocate the Value object used when moving items + Type = colp->GetResultType(); + + if (!(Valp = AllocateValue(g, Type, len, colp->GetPrecision()))) + return true; + + Klen = Valp->GetClen(); + Keys.Size = n * Klen; + + if (!PlgDBalloc(g, NULL, Keys)) { + sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, n); + return true; // Error + } // endif + + // Allocate the Valblock. The last parameter is to have rows filled + // by blanks (if true) or keep the zero ending char (if false). + // Currently we set it to true to be compatible with QRY blocks, + // and the one before last is to enable length/type checking, set to + // true if not a prefix key. + Kblp = AllocValBlock(g, To_Keys, Type, n, len, prec, !Prefix, true); + Asc = sm; // Sort mode: Asc=true Desc=false + Ndf = n; + + // Store this information to avoid sorting when already done + if (Asc) + IsSorted = colp->GetOpt() < 0; + +//MayHaveNulls = colp->HasNulls(); + return false; + } // end of Init + +#if defined(XMAP) +/***********************************************************************/ +/* KXYCOL MapInit: initialize and address storage. */ +/* Key length kln can be smaller than column length for CHAR columns. */ +/***********************************************************************/ +BYTE* KXYCOL::MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m) + { + int len = colp->GetLength(), prec = colp->GetPrecision(); + + if (n[3] && colp->GetLength() > n[3] + && colp->GetResultType() == TYPE_STRING) { + len = n[3]; + Prefix = true; + } // endif kln + + Type = colp->GetResultType(); + +#ifdef DEBTRACE + htrc("MapInit(%p): colp=%p type=%d n=%d len=%d m=%p\n", + this, colp, Type, n[0], len, m); +#endif + + // Allocate the Value object used when moving items + Valp = AllocateValue(g, Type, len, prec, NULL); + Klen = Valp->GetClen(); + + if (n[2]) { + Bkeys.Size = n[2] * Klen; + Bkeys.Memp = m; + Bkeys.Sub = true; + + // Allocate the Valblk containing initial block key values + Blkp = AllocValBlock(g, To_Bkeys, Type, n[2], len, prec, true, true); + } // endif nb + + Keys.Size = n[0] * Klen; + Keys.Memp = m + Bkeys.Size; + Keys.Sub = true; + + // Allocate the Valblock. Last two parameters are to have rows filled + // by blanks (if true) or keep the zero ending char (if false). + // Currently we set it to true to be compatible with QRY blocks, + // and last one to enable type checking (no conversion). + Kblp = AllocValBlock(g, To_Keys, Type, n[0], len, prec, true, true); + + if (n[1]) { + Koff.Size = n[1] * sizeof(int); + Koff.Memp = m + Bkeys.Size + Keys.Size; + Koff.Sub = true; + } // endif n[1] + + Ndf = n[0]; + IsSorted = colp->GetOpt() < 0; + return m + Bkeys.Size + Keys.Size + Koff.Size; + } // end of MapInit +#endif // XMAP + +/***********************************************************************/ +/* Allocate the offset block used by intermediate key columns. */ +/***********************************************************************/ +int *KXYCOL::MakeOffset(PGLOBAL g, int n) + { + if (!Kof) { + // Calculate the initial size of the offset + Koff.Size = (n + 1) * sizeof(int); + + // Allocate the required memory + if (!PlgDBalloc(g, NULL, Koff)) { + strcpy(g->Message, MSG(KEY_ALLOC_ERR)); + return NULL; // Error + } // endif + + } else if (n) { + // This is a reallocation call + PlgDBrealloc(g, NULL, Koff, (n + 1) * sizeof(int)); + } else + PlgDBfree(Koff); + + return (int*)Kof; + } // end of MakeOffset + +/***********************************************************************/ +/* Make a front end array of key values that are the first value of */ +/* each blocks (of size n). This to reduce paging in FastFind. */ +/***********************************************************************/ +bool KXYCOL::MakeBlockArray(PGLOBAL g, int nb, int size) + { + int i, k; + + // Calculate the size of the block array in the index + Bkeys.Size = nb * Klen; + + // Allocate the required memory + if (!PlgDBalloc(g, NULL, Bkeys)) { + sprintf(g->Message, MSG(KEY_ALLOC_ERROR), Klen, nb); + return true; // Error + } // endif + + // Allocate the Valblk used to contains initial block key values + Blkp = AllocValBlock(g, To_Bkeys, Type, nb, Klen, Kprec); + + // Populate the array with values + for (i = k = 0; i < nb; i++, k += size) + Blkp->SetValue(Kblp, i, k); + + return false; + } // end of MakeBlockArray + +/***********************************************************************/ +/* KXYCOL SetValue: read column value for nth array element. */ +/***********************************************************************/ +void KXYCOL::SetValue(PCOL colp, int i) + { +#if defined(_DEBUG) + assert (Kblp != NULL); +#endif + + Kblp->SetValue(colp->GetValue(), (int)i); + } // end of SetValue + +/***********************************************************************/ +/* InitFind: initialize finding the rank of column value in index. */ +/***********************************************************************/ +bool KXYCOL::InitFind(PGLOBAL g, PXOB xp) + { + if (xp->GetType() == TYPE_CONST) { + if (Kxp->Nth) + return true; + + Valp->SetValue_pval(xp->GetValue(), !Prefix); + } else { + xp->Reset(); + xp->Eval(g); + Valp->SetValue_pval(xp->GetValue(), false); +// Valp->SetValue_pval(xp->GetValue(), !Prefix); + } // endif Type + + return false; + } // end of InitFind + +/***********************************************************************/ +/* InitBinFind: initialize Value to the value pointed by vp. */ +/***********************************************************************/ +void KXYCOL::InitBinFind(void *vp) + { + Valp->SetBinValue(vp); + } // end of InitBinFind + +/***********************************************************************/ +/* KXYCOL FillValue: called by COLBLK::Eval when a column value is */ +/* already in storage in the corresponding KXYCOL. */ +/***********************************************************************/ +void KXYCOL::FillValue(PVAL valp) + { + valp->SetValue_pvblk(Kblp, Val_K); + } // end of FillValue + +/***********************************************************************/ +/* KXYCOL: Compare routine for one numeric value. */ +/***********************************************************************/ +int KXYCOL::Compare(int i1, int i2) + { + // Do the actual comparison between values. + register int k = (int)Kblp->CompVal((int)i1, (int)i2); + +#ifdef DEBUG2 + htrc("Compare done result=%d\n", k); +#endif + + return (Asc) ? k : -k; + } // end of Compare + +/***********************************************************************/ +/* KXYCOL: Compare the ith key to the stored Value. */ +/***********************************************************************/ +int KXYCOL::CompVal(int i) + { + // Do the actual comparison between numerical values. +#ifdef DEBUG2 + register int k = (int)Kblp->CompVal(Valp, (int)i); + + htrc("Compare done result=%d\n", k); + return k; +#endif + return (int)Kblp->CompVal(Valp, (int)i); + } // end of CompVal + +/***********************************************************************/ +/* KXYCOL: Compare the key to the stored block value. */ +/***********************************************************************/ +int KXYCOL::CompBval(int i) + { + // Do the actual comparison between key values. + return (int)Blkp->CompVal(Valp, (int)i); + } // end of CompBval + +/***********************************************************************/ +/* KXYCOL ReAlloc: ReAlloc To_Data if it is not suballocated. */ +/***********************************************************************/ +void KXYCOL::ReAlloc(PGLOBAL g, int n) + { + PlgDBrealloc(g, NULL, Keys, n * Klen); + Kblp->ReAlloc(To_Keys, n); + Ndf = n; + } // end of ReAlloc + +/***********************************************************************/ +/* KXYCOL FreeData: Free To_Keys if it is not suballocated. */ +/***********************************************************************/ +void KXYCOL::FreeData(void) + { + PlgDBfree(Keys); + Kblp = NULL; + PlgDBfree(Bkeys); + Blkp = NULL; + PlgDBfree(Koff); + Ndf = 0; + } // end of FreeData diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h new file mode 100644 index 00000000000..6ff2b4be237 --- /dev/null +++ b/storage/connect/xindex.h @@ -0,0 +1,500 @@ +/*************** Xindex H Declares Source Code File (.H) ***************/ +/* Name: XINDEX.H Version 3.5 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2004 - 2013 */ +/* */ +/* This file contains the XINDEX class declares. */ +/***********************************************************************/ +#ifndef __XINDEX_H__ +#define __XINDEX_H__ +#include "block.h" +#include "csort.h" /* Base class declares */ +#include "xtable.h" +#include "valblk.h" +#if defined(XMAP) +#include "maputil.h" +#endif // XMAP + +enum IDT {TYPE_IDX_ERROR = 0, /* Type not defined */ + TYPE_IDX_INDX = 4, /* Permanent standard index */ + TYPE_IDX_XROW = 5}; /* Permanent row index */ + +#if defined(XMAP) +typedef MEMMAP *MMP; +#endif // XMAP +typedef class INDEXDEF *PIXDEF; +typedef class KPARTDEF *PKPDEF; +typedef class XINDEX *PXINDEX; +typedef class XLOAD *PXLOAD; +typedef class KXYCOL *PXCOL; + +/***********************************************************************/ +/* Structures used when checking for possible indexing */ +/***********************************************************************/ +typedef struct index_col *PICOL; +typedef struct index_val *PIVAL; +typedef struct index_def *PINDX; +typedef struct indx_used *PXUSED; + +typedef struct index_val : public BLOCK { + index_val(PXOB xp) {Next = NULL; Xval = xp; Kp = NULL;} + PIVAL Next; // Next value + PXOB Xval; // To value or array + int *Kp; // The coordonates in a LSTBLK + } IVAL; + +typedef struct index_col : public BLOCK { + index_col(PCOL cp) + {Next = Nxtgrp = NULL; Colp = cp; Ngrp = N = 0; Vals = NULL;} + PICOL Next; // Next column + PICOL Nxtgrp; // Next group + PCOL Colp; // The column + PIVAL Vals; // To column values + int Ngrp; // Group number of values + int N; // Column number of values + } ICOL; + +typedef struct index_def : public BLOCK { + index_def(PIXDEF xdp) + {Next = NULL; Pxdf = xdp; Cols = NULL; Alloc = false;} + PINDX Next; + PIXDEF Pxdf; + PICOL Cols; + bool Alloc; // Must allocate values + } INDX; + +typedef struct index_off { + union { + struct {int Low; int High;}; + longlong Val; // File position + }; // end of union + } IOFF; + +/***********************************************************************/ +/* Index definition block. */ +/***********************************************************************/ +class DllExport INDEXDEF : public BLOCK { /* Index description block */ + friend class PLUGCAT; + friend class DOSDEF; + friend class ha_connect; + friend int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add); + public: + // Constructor + INDEXDEF(char *name, bool uniq = false, int n = 0); + + // Implementation + PIXDEF GetNext(void) {return Next;} + void SetNext(PIXDEF pxdf) {Next = pxdf;} + PSZ GetName(void) {return (PSZ)Name;} + bool IsUnique(void) {return Unique;} + bool IsAuto(void) {return AutoInc;} + void SetAuto(bool b) {AutoInc = b;} + void SetInvalid(bool b) {Invalid = b;} + int GetNparts(void) {return Nparts;} + int GetID(void) {return ID;} + void SetID(int n) {ID = n;} + PKPDEF GetToKeyParts(void) {return ToKeyParts;} + void SetToKeyParts(PKPDEF kp) {ToKeyParts = kp;} + void SetNParts(uint np) {Nparts = (signed)np;} + void SetMaxSame(int mxs) {MaxSame = mxs;} + void SetMxsame(PXINDEX x); +//int GetOffset(void) {return Offset;} +//void SetOffset(int off) {Offset = off;} +//int GetOffhigh(void) {return Offhigh;} +//void SetOffhigh(int hof) {Offhigh = hof;} +//int GetSize(void) {return Size;} +//void SetSize(int size) {Size = size;} + int GetMaxSame(void) {return MaxSame;} + bool Define(PGLOBAL g, void *memp, PTABDEF dfp, LPCSTR p); + PIXDEF GetIndexOf(PCOL colp, bool hd = false); + int IsIndexOf(PCOL colp); + PKXBASE CheckIndexing(PGLOBAL g, PTDBDOS tdbp); + PINDX CheckAND(PGLOBAL g, PINDX pix1, PINDX pix2); + PINDX CheckOR(PGLOBAL g, PINDX pix1, PINDX pix2); + PINDX CheckEQ(PGLOBAL g, PTDB tdbp, PXOB *arg, int op, int *kp = NULL); + bool TestEQ(PGLOBAL g, PTDB tdbp, PXOB *arg, int op, bool b = false); + + protected: + PIXDEF Next; /* To next block */ + PKPDEF ToKeyParts; /* To the key part definitions */ + char *Name; /* Index name */ + bool Unique; /* true if defined as unique */ + bool Invalid; /* true if marked as Invalid */ + bool AutoInc; /* true if unique key in auto increment */ + int Nparts; /* Number of key parts */ + int ID; /* Index ID number */ +//int Offset; /* Offset in index file */ +//int Offhigh; /* Offset high in big index file */ +//int Size; /* Size of index file */ + int MaxSame; /* Max number of same values */ + }; // end of INDEXDEF + +typedef struct indx_used : public BLOCK { + indx_used(PTDB tp, PIXDEF xdp, PCOL *cp, int k) + {Tname = (char*)tp->GetName(); Xname = xdp->GetName(); Cp = cp; K = k;} + PXUSED Next; + char *Tname; + PSZ Xname; + PCOL *Cp; + int K; + } XUSED; + +/***********************************************************************/ +/* Index Key Part definition block. */ +/***********************************************************************/ +class DllExport KPARTDEF : public BLOCK { /* Index Key Part desc block */ + friend class INDEXDEF; + friend class XINDEX; + friend class PLUGCAT; + friend class DOSDEF; + friend class ha_connect; + friend int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add); + public: + KPARTDEF(PSZ name, int n); // Constructor + + // Implementation + PKPDEF GetNext(void) {return Next;} + PSZ GetName(void) {return (PSZ)Name;} + int GetNcol(void) {return Ncol;} + void SetNext(PKPDEF pkdf) {Next = pkdf;} + void SetKlen(int len) {Klen = len;} + void SetMxsame(int mxs) {Mxsame = mxs;} + + protected: + PKPDEF Next; /* To next block */ + PSZ Name; /* Field name */ + int Mxsame; /* Field max same values */ + int Ncol; /* Field number */ + int Klen; /* Key length */ + }; // end of KPARTDEF + +/***********************************************************************/ +/* This is the XDB Index virtual base class declaration. */ +/***********************************************************************/ +class DllExport XXBASE : public CSORT, public BLOCK { + friend class INDEXDEF; + friend class KXYCOL; + public: + // Constructor + XXBASE(PTDBDOS tbxp, bool b); + + // Implementation + virtual IDT GetType(void) = 0; + virtual void Reset(void) = 0; + virtual bool IsMul(void) {return false;} + virtual bool IsRandom(void) {return true;} + virtual bool HaveSame(void) {return false;} + virtual int GetCurPos(void) {return Cur_K;} + virtual void SetNval(int n) {assert(n == 1);} + virtual void SetOp(OPVAL op) {Op = op;} + int GetNdif(void) {return Ndif;} + int GetNum_K(void) {return Num_K;} + int GetCur_K(void) {return Cur_K;} + int GetID(void) {return ID;} + void SetID(int id) {ID = id;} + void SetNth(int n) {Nth = n;} + int *GetPof(void) {return Pof;} + int *GetPex(void) {return Pex;} + void FreeIndex(void) {PlgDBfree(Index);} + + // Methods + virtual void Print(PGLOBAL g, FILE *f, uint n); + virtual void Print(PGLOBAL g, char *ps, uint z); + virtual bool Init(PGLOBAL g) = 0; + virtual int MaxRange(void) {return 1;} + virtual int Fetch(PGLOBAL g) = 0; + virtual bool NextVal(bool eq) {return true;} + virtual int FastFind(int nk) = 0; + virtual bool Reorder(PGLOBAL g) {return true;} + virtual int Range(PGLOBAL g, int limit = 0, bool incl = true) + {return -1;} // Means error + virtual int Qcompare(int *, int *) = 0; + virtual int GroupSize(void) {return 1;} + virtual void Close(void) = 0; + + protected: + // Members + PTDBASE Tbxp; // Points to calling table TDB + PXCOL To_KeyCol; // To KeyCol class list + MBLOCK Record; // Record allocation block + int* &To_Rec; // We are using ftell, fseek + int Cur_K; // Index of current record + int Old_K; // Index of last record + int Num_K; // Size of Rec_K pointer array + int Ndif; // Number of distinct values + int Bot; // Bottom of research index + int Top; // Top of research index + int Inf, Sup; // Used for block optimization + OPVAL Op; // Search operator + bool Mul; // true if multiple + bool Srtd; // true for sorted column + int Val_K; // Index of current value + int Nblk; // Number of blocks + int Sblk; // Block size + int Thresh; // Thresh for sorting join indexes + int ID; // Index ID number + int Nth; // Nth constant to fetch + }; // end of class XXBASE + +/***********************************************************************/ +/* This is the standard (multicolumn) Index class declaration. */ +/***********************************************************************/ +class DllExport XINDEX : public XXBASE { + friend class KXYCOL; + public: + // Constructor + XINDEX(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, + PCOL *cp, PXOB *xp = NULL, int k = 0); + + // Implementation + virtual IDT GetType(void) {return TYPE_IDX_INDX;} + virtual bool IsMul(void) {return (Nval < Nk) ? true : Mul;} + virtual bool HaveSame(void) {return Op == OP_SAME;} + virtual int GetCurPos(void) {return (Pex) ? Pex[Cur_K] : Cur_K;} + virtual void SetNval(int n) {Nval = n;} + int GetMaxSame(void) {return MaxSame;} +// int GetDefoff(void) {return Defoff;} +// int GetDefhigh(void) {return Defhigh;} +// int GetSize(void) {return Size;} + + // Methods + virtual void Reset(void); + virtual bool Init(PGLOBAL g); + virtual int Qcompare(int *, int *); + virtual int Fetch(PGLOBAL g); + virtual int FastFind(int nk); + virtual int GroupSize(void); + virtual int Range(PGLOBAL g, int limit = 0, bool incl = true); + virtual int MaxRange(void) {return MaxSame;} + virtual int ColMaxSame(PXCOL kp); + virtual void Close(void); + virtual bool NextVal(bool eq); + virtual bool Make(PGLOBAL g, PIXDEF sxp); + virtual bool SaveIndex(PGLOBAL g, PIXDEF sxp); + virtual bool Reorder(PGLOBAL g); + bool GetAllSizes(PGLOBAL g, int &ndif, int &numk); + + protected: + bool NextValDif(void); + + // Members + PIXDEF Xdp; // To index definition + PTDBDOS Tdbp; // Points to calling table TDB + PXLOAD X; // To XLOAD class + PXCOL To_LastCol; // To the last key part block + PXCOL To_LastVal; // To the last used key part block + PCOL *To_Cols; // To array of indexed columns + PXOB *To_Vals; // To array of column values + int Nk; // The number of indexed columns + int Nval; // The number of used columns + int Incr; // Increment of record position +//int Defoff; // Offset of definition in index file +//int Defhigh; // High order of offset big value +//int Size; // Size of definition in index file + int MaxSame; // Max number of same values + }; // end of class XINDEX + +/***********************************************************************/ +/* This is the fast single column index class declaration. */ +/***********************************************************************/ +class DllExport XINDXS : public XINDEX { + friend class KXYCOL; + public: + // Constructor + XINDXS(PTDBDOS tdbp, PIXDEF xdp, PXLOAD pxp, PCOL *cp, PXOB *xp = NULL); + + // Implementation + virtual void SetNval(int n) {assert(n == 1);} + + // Methods + virtual int Qcompare(int *, int *); + virtual int Fetch(PGLOBAL g); + virtual int FastFind(int nk); + virtual bool NextVal(bool eq); + virtual int Range(PGLOBAL g, int limit = 0, bool incl = true); + virtual int GroupSize(void); + + protected: + // Members + }; // end of class XINDXS + +/***********************************************************************/ +/* This is the saving/loading index utility base class. */ +/***********************************************************************/ +class DllExport XLOAD : public BLOCK { + friend class XINDEX; + friend class XBIGEX; + friend class XBIGXS; + public: + // Constructor + XLOAD(void); + + // Methods + virtual bool Open(PGLOBAL g, char *filename, int id, MODE mode) = 0; + virtual bool Seek(PGLOBAL g, int low, int high, int origin) = 0; + virtual bool Read(PGLOBAL g, void *buf, int n, int size) = 0; + virtual int Write(PGLOBAL g, void *buf, int n, + int size, bool& rc) = 0; + virtual void Close(char *fn, int id) = 0; + virtual void Close(void); +#if defined(XMAP) + virtual void *FileView(PGLOBAL g, char *fn) = 0; +#endif // XMAP + + protected: + // Members +#if defined(WIN32) + HANDLE Hfile; // Handle to file or map +#if defined(XMAP) + void *ViewBase; // Mapped view base address +#endif // XMAP +#else // UNIX + int Hfile; // Descriptor to file or map +#endif // UNIX + IOFF NewOff; // New offset + }; // end of class XLOAD + +/***********************************************************************/ +/* This is the saving/loading indexes utility class. */ +/***********************************************************************/ +class DllExport XFILE : public XLOAD { + public: + // Constructor + XFILE(void); + + // Methods + virtual bool Open(PGLOBAL g, char *filename, int id, MODE mode); + virtual bool Seek(PGLOBAL g, int low, int high, int origin); + virtual bool Read(PGLOBAL g, void *buf, int n, int size); + virtual int Write(PGLOBAL g, void *buf, int n, int size, bool& rc); + virtual void Close(char *fn, int id); + virtual void Close(void); +#if defined(XMAP) + virtual void *FileView(PGLOBAL g, char *fn); +#endif // XMAP + + protected: + // Members + FILE *Xfile; // Index stream file +#if defined(XMAP) + MMP Mmp; // To mapped index file +#endif // XMAP + }; // end of class XFILE + +/***********************************************************************/ +/* This is the saving/loading huge indexes utility class. */ +/***********************************************************************/ +class DllExport XHUGE : public XLOAD { + public: + // Constructor + XHUGE(void) : XLOAD() {} + + // Methods + virtual bool Open(PGLOBAL g, char *filename, int id, MODE mode); + virtual bool Seek(PGLOBAL g, int low, int high, int origin); + virtual bool Read(PGLOBAL g, void *buf, int n, int size); + virtual int Write(PGLOBAL g, void *buf, int n, int size, bool& rc); + virtual void Close(char *fn, int id); +#if defined(XMAP) + virtual void *FileView(PGLOBAL g, char *fn); +#endif // XMAP + + protected: + // Members + }; // end of class XHUGE + +/***********************************************************************/ +/* This is the XDB index for columns containing ROWID values. */ +/***********************************************************************/ +class DllExport XXROW : public XXBASE { + friend class KXYCOL; + public: + // Constructor + XXROW(PTDBDOS tbxp); + + // Implementation + virtual IDT GetType(void) {return TYPE_IDX_XROW;} + virtual void Reset(void); + + // Methods + virtual bool Init(PGLOBAL g); + virtual int Fetch(PGLOBAL g); + virtual int FastFind(int nk); + virtual int MaxRange(void) {return 1;} + virtual int Range(PGLOBAL g, int limit = 0, bool incl = true); + virtual int Qcompare(int *, int *) {assert(false); return 0;} + virtual void Close(void) {} + + protected: + // Members + PTDBDOS Tdbp; // Points to calling table TDB + PVAL Valp; // The value to match in index + }; // end of class XXROW + +/***********************************************************************/ +/* Definition of class KXYCOL used to store values of indexed columns */ +/***********************************************************************/ +class KXYCOL: public BLOCK { + friend class INDEXDEF; + friend class XINDEX; + friend class XINDXS; + friend class XBIGEX; + friend class XBIGXS; + friend class TDBDOS; + public: + // Constructors + KXYCOL(PKXBASE kp); + + // Implementation + int GetType(void) {return Type;} + void SetValue(PCOL colp, int i); + + public: + // Methods + virtual bool Init(PGLOBAL g, PCOL colp, int n, bool sm, int kln); + virtual bool InitFind(PGLOBAL g, PXOB xp); + virtual void ReAlloc(PGLOBAL g, int n); + virtual void FreeData(void); + virtual void FillValue(PVAL valp); + virtual int CompVal(int i); + void InitBinFind(void *vp); + bool MakeBlockArray(PGLOBAL g, int nb, int size); + int Compare(int i1, int i2); + int CompBval(int i); + void Save(int i) {Valp->SetBinValue(Kblp->GetValPtr(i));} + void Restore(int j) {Kblp->SetValue(Valp, j);} + void Move(int j, int k) {Kblp->Move(k, j);} + + // Specific functions +#if defined(XMAP) + BYTE *MapInit(PGLOBAL g, PCOL colp, int *n, BYTE *m); +#endif // XMAP + int *MakeOffset(PGLOBAL g, int n); + + protected: + // Members + PXCOL Next; // To next in the key part list + PXCOL Previous; // To previous in the key part list + PKXBASE Kxp; // To the INDEX class block + PCOL Colp; // To matching object if a column + bool IsSorted; // true if column is already sorted + bool Asc; // true for ascending sort, false for Desc + MBLOCK Keys; // Data array allocation block + void* &To_Keys; // To data array + PVBLK Kblp; // To Valblock of the data array + MBLOCK Bkeys; // Block array allocation block + void* &To_Bkeys; // To block array + PVBLK Blkp; // To Valblock of the block array + PVAL Valp; // Value use by Find + int Klen; // Length of character string or num value + int Kprec; // The Value(s) precision or CI + int Type; // The Value(s) type + bool Prefix; // Key on CHAR column prefix + MBLOCK Koff; // Offset allocation block + CPINT &Kof; // Reference to offset array + int Val_K; // Index of current column value + int Ndf; // Number of stored values + int Mxs; // Max same for this column + }; // end of class KXYCOL + +#endif // __XINDEX_H__ diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp new file mode 100644 index 00000000000..cd8af248005 --- /dev/null +++ b/storage/connect/xobject.cpp @@ -0,0 +1,184 @@ +/************ Xobject C++ Functions Source Code File (.CPP) ************/ +/* Name: XOBJECT.CPP Version 2.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* This file contains base XOBJECT class functions. */ +/* Also here is the implementation of the CONSTANT class. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include mariaDB header file. */ +/***********************************************************************/ +#include "my_global.h" + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xobject.h" + +/***********************************************************************/ +/* Macro definitions. */ +/***********************************************************************/ +#if defined(_DEBUG) || defined(DEBTRACE) +#define ASSERT(B) assert(B); +#else +#define ASSERT(B) +#endif + +/***********************************************************************/ +/* The one and only needed void object. */ +/***********************************************************************/ +XVOID Xvoid; +PXOB const pXVOID = &Xvoid; // Pointer used by other classes + +/* ------------------------- Class XOBJECT --------------------------- */ + +/***********************************************************************/ +/* GetCharValue: returns the Result value as a char string. */ +/* Using GetCharValue provides no conversion from numeric types. */ +/***********************************************************************/ +PSZ XOBJECT::GetCharValue(void) + { + ASSERT(Value) + return Value->GetCharValue(); + } // end of GetCharValue() + +/***********************************************************************/ +/* GetShortValue: returns the Result value as a short integer. */ +/***********************************************************************/ +short XOBJECT::GetShortValue(void) + { + ASSERT(Value) + return Value->GetShortValue(); + } // end of GetShortValue + +/***********************************************************************/ +/* GetIntValue: returns the Result value as a int integer. */ +/***********************************************************************/ +int XOBJECT::GetIntValue(void) + { + ASSERT(Value) + return Value->GetIntValue(); + } // end of GetIntValue + +/***********************************************************************/ +/* GetFloatValue: returns the Result value as a double float. */ +/***********************************************************************/ +double XOBJECT::GetFloatValue(void) + { + ASSERT(Value) + return Value->GetFloatValue(); + } // end of GetFloatValue + +/* ------------------------- Class CONSTANT -------------------------- */ + +/***********************************************************************/ +/* CONSTANT public constructor. */ +/***********************************************************************/ +CONSTANT::CONSTANT(PGLOBAL g, void *value, short type) + { + if (!(Value = AllocateValue(g, value, (int)type))) + longjmp(g->jumper[g->jump_level], TYPE_CONST); + + Constant = true; + } // end of CONSTANT constructor + +/***********************************************************************/ +/* CONSTANT public constructor. */ +/***********************************************************************/ +CONSTANT::CONSTANT(PGLOBAL g, int n) + { + if (!(Value = AllocateValue(g, &n, TYPE_INT))) + longjmp(g->jumper[g->jump_level], TYPE_CONST); + + Constant = true; + } // end of CONSTANT constructor + +/***********************************************************************/ +/* GetLengthEx: returns an evaluation of the constant string length. */ +/* Note: When converting from token to string, length has to be */ +/* specified but we need the domain length, not the value length. */ +/***********************************************************************/ +int CONSTANT::GetLengthEx(void) + { + return Value->GetValLen(); + } // end of GetLengthEx + +/***********************************************************************/ +/* Convert a constant to the given type. */ +/***********************************************************************/ +void CONSTANT::Convert(PGLOBAL g, int newtype) + { + if (Value->GetType() != newtype) + if (!(Value = AllocateValue(g, Value, newtype))) + longjmp(g->jumper[g->jump_level], TYPE_CONST); + + } // end of Convert + +/***********************************************************************/ +/* Compare: returns true if this object is equivalent to xp. */ +/***********************************************************************/ +bool CONSTANT::Compare(PXOB xp) + { + if (this == xp) + return true; + else if (xp->GetType() != TYPE_CONST) + return false; + else + return Value->IsEqual(xp->GetValue(), true); + + } // end of Compare + +/***********************************************************************/ +/* Rephrase: temporary implementation used by PlugRephraseSQL. */ +/***********************************************************************/ +bool CONSTANT::Rephrase(PGLOBAL g, PSZ work) + { + switch (Value->GetType()) { + case TYPE_STRING: + sprintf(work + strlen(work), "'%s'", Value->GetCharValue()); + break; + case TYPE_SHORT: + sprintf(work + strlen(work), "%hd", Value->GetShortValue()); + break; + case TYPE_INT: + case TYPE_DATE: + sprintf(work + strlen(work), "%d", Value->GetIntValue()); + break; + case TYPE_FLOAT: + sprintf(work + strlen(work), "%lf", Value->GetFloatValue()); + break; + case TYPE_BIGINT: + sprintf(work + strlen(work), "%lld", Value->GetBigintValue()); + break; + case TYPE_TINY: + sprintf(work + strlen(work), "%d", Value->GetTinyValue()); + break; + default: + sprintf(g->Message, MSG(BAD_CONST_TYPE), Value->GetType()); + return false; + } // endswitch + + return false; + } // end of Rephrase + +/***********************************************************************/ +/* Make file output of a constant object. */ +/***********************************************************************/ +void CONSTANT::Print(PGLOBAL g, FILE *f, uint n) + { + Value->Print(g, f, n); + } /* end of Print */ + +/***********************************************************************/ +/* Make string output of a constant object. */ +/***********************************************************************/ +void CONSTANT::Print(PGLOBAL g, char *ps, uint z) + { + Value->Print(g, ps, z); + } /* end of Print */ diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h new file mode 100644 index 00000000000..c77fb703823 --- /dev/null +++ b/storage/connect/xobject.h @@ -0,0 +1,136 @@ +/*************** Xobject H Declares Source Code File (.H) **************/ +/* Name: XOBJECT.H Version 2.3 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */ +/* */ +/* This file contains the XOBJECT and derived classes declares. */ +/***********************************************************************/ + +#ifndef __XOBJECT__H +#define __XOBJECT__H + +/***********************************************************************/ +/* Include required application header files */ +/* block.h is header containing Block global declarations. */ +/***********************************************************************/ +#include "block.h" +#include "valblk.h" // includes value.h + +/***********************************************************************/ +/* Types used in some class definitions. */ +/***********************************************************************/ +//typedef struct _tabdesc *PTABD; // For friend setting + +/***********************************************************************/ +/* The pointer to the one and only needed void object. */ +/***********************************************************************/ +extern PXOB const pXVOID; + +/***********************************************************************/ +/* Class XOBJECT is the base class for all classes that can be used */ +/* in evaluation operations: FILTER, EXPRESSION, SCALF, FNC, COLBLK, */ +/* SELECT, FILTER as well as all the constant object types. */ +/***********************************************************************/ +class DllExport XOBJECT : public BLOCK { + public: + XOBJECT(void) {Value = NULL; Constant = false;} + + // Implementation + PVAL GetValue(void) {return Value;} + bool IsConstant(void) {return Constant;} + virtual int GetType(void) {return TYPE_XOBJECT;} + virtual int GetResultType(void) {return TYPE_VOID;} + virtual int GetKey(void) {return 0;} +#if defined(_DEBUG) + virtual void SetKey(int k) {assert(false);} +#else // !_DEBUG + virtual void SetKey(int k) {} // Only defined for COLBLK +#endif // !_DEBUG + virtual int GetLength(void) = 0; + virtual int GetLengthEx(void) = 0; + virtual PSZ GetCharValue(void); + virtual short GetShortValue(void); + virtual int GetIntValue(void); + virtual double GetFloatValue(void); + virtual int GetPrecision(void) = 0; + + // Methods + virtual void Reset(void) {} + virtual bool Compare(PXOB) = 0; + virtual bool Init(PGLOBAL) {return false;} + virtual bool Eval(PGLOBAL) {return false;} + virtual bool SetFormat(PGLOBAL, FORMAT&) = 0; + virtual int CheckColumn(PGLOBAL, PSQL, PXOB &, int &) {return 0;} + virtual int RefNum(PSQL) {return 0;} + virtual void AddTdb(PSQL, PTDB *, int&) {} + virtual PXOB SetSelect(PGLOBAL, PSQL, bool) {return this;} + virtual PXOB CheckSubQuery(PGLOBAL, PSQL) {return this;} + virtual bool CheckLocal(PTDB) {return true;} + virtual int CheckSpcCol(PTDB, int) {return 2;} + virtual bool CheckSort(PTDB) {return false;} + virtual bool VerifyColumn(PTBX txp) {return false;} + virtual bool VerifyTdb(PTDB& tdbp) {return false;} + virtual bool IsColInside(PCOL colp) {return false;} + virtual void MarkCol(ushort) {} + + protected: + PVAL Value; // The current value of the object. + bool Constant; // true for an object having a constant value. + }; // end of class XOBJECT + +/***********************************************************************/ +/* Class XVOID: represent a void (null) object. */ +/* Used to represent a void parameter for count(*) or for a filter. */ +/***********************************************************************/ +class DllExport XVOID : public XOBJECT { + public: + XVOID(void) {Constant = true;} + + // Implementation + virtual int GetType(void) {return TYPE_VOID;} + virtual int GetLength(void) {return 0;} + virtual int GetLengthEx(void) {return 0;} + virtual PSZ GetCharValue(void) {return NULL;} + virtual int GetIntValue(void) {return 0;} + virtual double GetFloatValue(void) {return 0.0;} + virtual int GetPrecision() {return 0;} + + // Methods + virtual bool Compare(PXOB xp) {return xp->GetType() == TYPE_VOID;} + virtual bool SetFormat(PGLOBAL, FORMAT&) {return true;} + virtual int CheckSpcCol(PTDB, int) {return 0;} + }; // end of class XVOID + + +/***********************************************************************/ +/* Class CONSTANT: represents a constant XOBJECT of any value type. */ +/* Note that the CONSTANT class is a friend of the VALUE class; */ +/***********************************************************************/ +class DllExport CONSTANT : public XOBJECT { + public: + CONSTANT(PGLOBAL g, void *value, short type); + CONSTANT(PGLOBAL g, int n); + CONSTANT(PVAL valp) {Value = valp; Constant = true;} + + // Implementation + virtual int GetType(void) {return TYPE_CONST;} + virtual int GetResultType(void) {return Value->Type;} + virtual int GetLength(void) {return Value->GetValLen();} + virtual int GetPrecision() {return Value->GetValPrec();} + virtual int GetLengthEx(void); + + // Methods + virtual bool Compare(PXOB xp); + virtual bool SetFormat(PGLOBAL g, FORMAT& fmt) + {return Value->SetConstFormat(g, fmt);} + virtual int CheckSpcCol(PTDB, int) {return 1;} + void Convert(PGLOBAL g, int newtype); + bool Rephrase(PGLOBAL g, PSZ work); + void SetValue(PVAL vp) {Value = vp;} + virtual bool VerifyColumn(PTBX txp) {return true;} + virtual bool VerifyTdb(PTDB& tdbp) {return true;} + virtual void Print(PGLOBAL g, FILE *, uint); + virtual void Print(PGLOBAL g, char *, uint); + }; // end of class CONSTANT + +#endif diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h new file mode 100644 index 00000000000..8475c46d52d --- /dev/null +++ b/storage/connect/xtable.h @@ -0,0 +1,278 @@ +/**************** Table H Declares Source Code File (.H) ***************/ +/* Name: TABLE.H Version 2.2 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2012 */ +/* */ +/* This file contains the TBX, OPJOIN and TDB class definitions. */ +/***********************************************************************/ +#if !defined(TABLE_DEFINED) +#define TABLE_DEFINED + + +/***********************************************************************/ +/* Include required application header files */ +/* block.h is header containing Block global declarations. */ +/***********************************************************************/ +#include "assert.h" +#include "block.h" +#include "colblk.h" +#include "m_ctype.h" + +//pedef class INDEXDEF *PIXDEF; +typedef char *PFIL; // Specific to CONNECT +typedef class TDBCAT *PTDBCAT; +typedef class CATCOL *PCATCOL; + +/***********************************************************************/ +/* Definition of class TBX (pure virtual class for TDB and OPJOIN) */ +/***********************************************************************/ +class DllExport TBX: public BLOCK { // Base class for OPJOIN and TDB classes. + public: + // Constructors + TBX(void); + TBX(PTBX txp); + + // Implementation + inline PTBX GetOrig(void) {return To_Orig;} + inline TUSE GetUse(void) {return Use;} + inline void SetUse(TUSE n) {Use = n;} + inline PFIL GetFilter(void) {return To_Filter;} + inline void SetOrig(PTBX txp) {To_Orig = txp;} + inline void SetFilter(PFIL fp) {To_Filter = fp;} +//inline JTYPE GetJtype(void) {return Jtype;} +//inline void SetJtype(JTYPE jt) {Jtype = jt;} +//inline PFIL GetNoleft(void) {return To_Noleft;} +//inline void SetNoleft(PFIL fp) {To_Noleft = fp;} + + // Methods + virtual bool IsSame(PTBX tp) {return tp == this;} +//virtual bool Include(PTBX tbxp) = 0; +//virtual bool CheckFilter(void) = 0; + virtual int GetTdb_No(void) = 0; // Convenience during conversion + virtual PTDB GetNext(void) = 0; +//virtual int GetMaxSame(PGLOBAL) = 0; + virtual int Cardinality(PGLOBAL) = 0; + virtual int GetMaxSize(PGLOBAL) = 0; + virtual int GetProgMax(PGLOBAL) = 0; + virtual int GetProgCur(void) = 0; + virtual int GetBadLines(void) {return 0;} +//virtual bool IsJoin(void) = 0; + virtual PTBX Copy(PTABS t) = 0; + + protected: +//virtual void PrepareFilters(PGLOBAL g) = 0; + + protected: + // Members + PTBX To_Orig; // Pointer to original if it is a copy + PFIL To_Filter; +//PFIL To_Noleft; // To filter not involved in LEFT JOIN +//JTYPE Jtype; + TUSE Use; + }; // end of class TBX + +/***********************************************************************/ +/* Definition of class TDB with all its method functions. */ +/***********************************************************************/ +class DllExport TDB: public TBX { // Table Descriptor Block. + public: + // Constructors + TDB(PTABDEF tdp = NULL); + TDB(PTDB tdbp); + + // Implementation + static void SetTnum(int n) {Tnum = n;} + inline LPCSTR GetName(void) {return Name;} + inline PTABLE GetTable(void) {return To_Table;} + inline PCOL GetColumns(void) {return Columns;} + inline int GetDegree(void) {return Degree;} + inline MODE GetMode(void) {return Mode;} + inline void SetNext(PTDB tdbp) {Next = tdbp;} + inline void SetName(LPCSTR name) {Name = name;} + inline void SetTable(PTABLE tablep) {To_Table = tablep;} + inline void SetColumns(PCOL colp) {Columns = colp;} + inline void SetDegree(int degree) {Degree = degree;} + inline void SetMode(MODE mode) {Mode = mode;} + + //Properties + virtual int GetTdb_No(void) {return Tdb_No;} + virtual PTDB GetNext(void) {return Next;} + virtual PCATLG GetCat(void) {return NULL;} + + // Methods + virtual AMT GetAmType(void) {return TYPE_AM_ERROR;} + virtual bool GetBlockValues(PGLOBAL g) {return false;} + virtual int Cardinality(PGLOBAL g) {return (g) ? -1 : 0;} + virtual int RowNumber(PGLOBAL g, bool b = false); + virtual bool IsReadOnly(void) {return true;} + virtual const CHARSET_INFO *data_charset() { return NULL; } + virtual PTDB Duplicate(PGLOBAL g) {return NULL;} + virtual PTDB CopyOne(PTABS t) {return this;} + virtual PTBX Copy(PTABS t); + virtual void PrintAM(FILE *f, char *m) + {fprintf(f, "%s AM(%d)\n", m, GetAmType());} + virtual void Print(PGLOBAL g, FILE *f, uint n); + virtual void Print(PGLOBAL g, char *ps, uint z); + + // Database pure virtual routines + virtual PCOL ColDB(PGLOBAL g, PSZ name, int num) = 0; + virtual void MarkDB(PGLOBAL, PTDB) = 0; + virtual bool OpenDB(PGLOBAL) = 0; + virtual int ReadDB(PGLOBAL) = 0; + virtual int WriteDB(PGLOBAL) = 0; + virtual int DeleteDB(PGLOBAL, int) = 0; + virtual void CloseDB(PGLOBAL) = 0; + virtual int CheckWrite(PGLOBAL g) {return 0;} + + // Database routines + bool OpenTable(PGLOBAL g, PSQL sqlp, MODE mode); + void CloseTable(PGLOBAL g); + + protected: + // Members + static int Tnum; // Used to generate Tdb_no's + const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN + PTDB Next; // Next in linearized queries + PTABLE To_Table; // Points to the XTAB object + LPCSTR Name; // Table name + PCOL Columns; // Points to the first column of the table + MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete + int Degree; // Number of columns + }; // end of class TDB + +/***********************************************************************/ +/* This is the base class for all query tables (except decode). */ +/***********************************************************************/ +class DllExport TDBASE : public TDB { + friend class INDEXDEF; + friend class XINDEX; + friend class XINDXS; + public: + // Constructor + TDBASE(PTABDEF tdp = NULL); + TDBASE(PTDBASE tdbp); + + // Implementation + inline int GetKnum(void) {return Knum;} + inline PTABDEF GetDef(void) {return To_Def;} + inline PKXBASE GetKindex(void) {return To_Kindex;} + inline PCOL GetSetCols(void) {return To_SetCols;} + inline void SetSetCols(PCOL colp) {To_SetCols = colp;} + + // Properties + void SetKindex(PKXBASE kxp); + PCOL Key(int i) {return (To_Key_Col) ? To_Key_Col[i] : NULL;} + + // Methods + virtual bool IsUsingTemp(PGLOBAL g) {return false;} + virtual PCATLG GetCat(void); + virtual PSZ GetPath(void); + virtual void PrintAM(FILE *f, char *m); + virtual RECFM GetFtype(void) {return RECFM_NAF;} + virtual int GetAffectedRows(void) {return -1;} + virtual int GetRecpos(void) = 0; + virtual bool SetRecpos(PGLOBAL g, int recpos); + virtual bool IsReadOnly(void) {return Read_Only;} + virtual CHARSET_INFO *data_charset() + { + /* + If no DATA_CHARSET is specified, we assume that character + set of the remote data is the same with CHARACTER SET + definition of the SQL column. + */ + return m_data_charset ? m_data_charset : &my_charset_bin; + } + virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);} + virtual int GetProgCur(void) {return GetRecpos();} + virtual PSZ GetFile(PGLOBAL g) {return "Not a file";} + virtual int GetRemote(void) {return 0;} + virtual void SetFile(PGLOBAL g, PSZ fn) {} + virtual void ResetDB(void) {} + virtual void ResetSize(void) {MaxSize = -1;} + virtual void RestoreNrec(void) {} + virtual int ResetTableOpt(PGLOBAL g, bool dox); + + // Database routines + virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); + virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int) + {assert(false); return NULL;} + virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp); + virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLUMN cp); + virtual void MarkDB(PGLOBAL g, PTDB tdb2); + + protected: + // Members + PTABDEF To_Def; // Points to catalog description block + PXOB *To_Link; // Points to column of previous relations + PCOL *To_Key_Col; // Points to key columns in current file + PKXBASE To_Kindex; // Points to table key index + PCOL To_SetCols; // Points to updated columns + int MaxSize; // Max size in number of lines + int Knum; // Size of key arrays + bool Read_Only; // True for read only tables + const CHARSET_INFO *m_data_charset; + }; // end of class TDBASE + +/***********************************************************************/ +/* The abstract base class declaration for the catalog tables. */ +/***********************************************************************/ +class TDBCAT : public TDBASE { + friend class CATCOL; + public: + // Constructor + TDBCAT(PTABDEF tdp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_CAT;} + + // Methods + virtual int GetRecpos(void) {return N;} + virtual int GetProgCur(void) {return N;} + virtual int RowNumber(PGLOBAL g, bool b = false) {return N + 1;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); + virtual int DeleteDB(PGLOBAL g, int irc); + virtual void CloseDB(PGLOBAL g); + + protected: + // Specific routines + virtual PQRYRES GetResult(PGLOBAL g) = 0; + bool Initialize(PGLOBAL g); + bool InitCol(PGLOBAL g); + + // Members + PQRYRES Qrp; + int N; // Row number + bool Init; + }; // end of class TDBCAT + +/***********************************************************************/ +/* Class CATCOL: ODBC info column. */ +/***********************************************************************/ +class CATCOL : public COLBLK { + friend class TDBCAT; + public: + // Constructors + CATCOL(PCOLDEF cdp, PTDB tdbp, int n); + + // Implementation + virtual int GetAmType(void) {return TYPE_AM_ODBC;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + + protected: + CATCOL(void) {} // Default constructor not to be used + + // Members + PTDBCAT Tdbp; // Points to ODBC table block + PCOLRES Crp; // The column data array + int Flag; + }; // end of class CATCOL + +#endif // TABLE_DEFINED diff --git a/win/packaging/CPackWixConfig.cmake b/win/packaging/CPackWixConfig.cmake index 356d6ef4b89..e363fb97a28 100644 --- a/win/packaging/CPackWixConfig.cmake +++ b/win/packaging/CPackWixConfig.cmake @@ -9,7 +9,7 @@ IF(ESSENTIALS) ENDIF()
ELSE()
SET(CPACK_COMPONENTS_USED
- "Server;Client;Development;SharedLibraries;Embedded;Documentation;IniFiles;Readme;Debuginfo;Common")
+ "Server;Client;Development;SharedLibraries;Embedded;Documentation;IniFiles;Readme;Debuginfo;Common;connect_engine")
ENDIF()
SET( WIX_FEATURE_MySQLServer_EXTRA_FEATURES "DBInstance;SharedClientServerComponents")
@@ -62,6 +62,12 @@ SET(CPACK_COMPONENT_GROUP_MYSQLSERVER_DESCRIPTION "Install server") SET(CPACK_COMPONENT_DATAFILES_DESCRIPTION "Server data files" )
SET(CPACK_COMPONENT_DATAFILES_HIDDEN 1)
+ #Subfeature "Connect Engine"
+ SET(CPACK_COMPONENT_CONNECT_ENGINE_GROUP "MySQLServer")
+ SET(CPACK_COMPONENT_CONNECT_ENGINE_DISPLAY_NAME "Server data files")
+ SET(CPACK_COMPONENT_CONNECT_ENGINE_DESCRIPTION "Server data files" )
+ SET(CPACK_COMPONENT_CONNECT_ENGINE_HIDDEN 1)
+
#Feature "Devel"
SET(CPACK_COMPONENT_GROUP_DEVEL_DISPLAY_NAME "Development Components")
|