From 80f57fae60ef313766d9e8e9e4706ff9e041076b Mon Sep 17 00:00:00 2001 From: vhermecz Date: Tue, 31 Jan 2023 20:20:39 +0100 Subject: [PATCH] Initial stage as of v0.999991 --- COPYING | 53 + ChangeLog | 350 + GPL | 340 + MANIFEST | 23 + README | 114 + doc/odbc.html | 1353 +++ ext/extconf.rb | 157 + ext/init.c | 197 + ext/odbc.c | 9475 +++++++++++++++++++++ ext/utf8/extconf.rb | 158 + ext/utf8/init.c | 12 + ext/utf8/odbc.c | 17 + lib/cqgen.rb | 561 ++ ruby-odbc-0.999991/COPYING | 53 + ruby-odbc-0.999991/ChangeLog | 350 + ruby-odbc-0.999991/GPL | 340 + ruby-odbc-0.999991/MANIFEST | 23 + ruby-odbc-0.999991/README | 114 + ruby-odbc-0.999991/doc/odbc.html | 1353 +++ ruby-odbc-0.999991/ext/extconf.rb | 157 + ruby-odbc-0.999991/ext/init.c | 197 + ruby-odbc-0.999991/ext/odbc.c | 9475 +++++++++++++++++++++ ruby-odbc-0.999991/ext/utf8/extconf.rb | 158 + ruby-odbc-0.999991/ext/utf8/init.c | 12 + ruby-odbc-0.999991/ext/utf8/odbc.c | 17 + ruby-odbc-0.999991/lib/cqgen.rb | 561 ++ ruby-odbc-0.999991/ruby-odbc.gemspec | 16 + ruby-odbc-0.999991/test/00connect.rb | 1 + ruby-odbc-0.999991/test/10create_table.rb | 1 + ruby-odbc-0.999991/test/20insert.rb | 6 + ruby-odbc-0.999991/test/30select.rb | 69 + ruby-odbc-0.999991/test/40update.rb | 4 + ruby-odbc-0.999991/test/45delete.rb | 8 + ruby-odbc-0.999991/test/50drop_table.rb | 3 + ruby-odbc-0.999991/test/70close.rb | 1 + ruby-odbc-0.999991/test/test.rb | 32 + ruby-odbc-0.999991/test/utf8/test.rb | 32 + ruby-odbc.gemspec | 16 + test/00connect.rb | 1 + test/10create_table.rb | 1 + test/20insert.rb | 6 + test/30select.rb | 69 + test/40update.rb | 4 + test/45delete.rb | 8 + test/50drop_table.rb | 3 + test/70close.rb | 1 + test/test.rb | 32 + test/utf8/test.rb | 32 + 48 files changed, 25966 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 GPL create mode 100644 MANIFEST create mode 100644 README create mode 100644 doc/odbc.html create mode 100644 ext/extconf.rb create mode 100644 ext/init.c create mode 100644 ext/odbc.c create mode 100644 ext/utf8/extconf.rb create mode 100644 ext/utf8/init.c create mode 100644 ext/utf8/odbc.c create mode 100644 lib/cqgen.rb create mode 100644 ruby-odbc-0.999991/COPYING create mode 100644 ruby-odbc-0.999991/ChangeLog create mode 100644 ruby-odbc-0.999991/GPL create mode 100644 ruby-odbc-0.999991/MANIFEST create mode 100644 ruby-odbc-0.999991/README create mode 100644 ruby-odbc-0.999991/doc/odbc.html create mode 100644 ruby-odbc-0.999991/ext/extconf.rb create mode 100644 ruby-odbc-0.999991/ext/init.c create mode 100644 ruby-odbc-0.999991/ext/odbc.c create mode 100644 ruby-odbc-0.999991/ext/utf8/extconf.rb create mode 100644 ruby-odbc-0.999991/ext/utf8/init.c create mode 100644 ruby-odbc-0.999991/ext/utf8/odbc.c create mode 100644 ruby-odbc-0.999991/lib/cqgen.rb create mode 100644 ruby-odbc-0.999991/ruby-odbc.gemspec create mode 100644 ruby-odbc-0.999991/test/00connect.rb create mode 100644 ruby-odbc-0.999991/test/10create_table.rb create mode 100644 ruby-odbc-0.999991/test/20insert.rb create mode 100644 ruby-odbc-0.999991/test/30select.rb create mode 100644 ruby-odbc-0.999991/test/40update.rb create mode 100644 ruby-odbc-0.999991/test/45delete.rb create mode 100644 ruby-odbc-0.999991/test/50drop_table.rb create mode 100644 ruby-odbc-0.999991/test/70close.rb create mode 100644 ruby-odbc-0.999991/test/test.rb create mode 100644 ruby-odbc-0.999991/test/utf8/test.rb create mode 100644 ruby-odbc.gemspec create mode 100644 test/00connect.rb create mode 100644 test/10create_table.rb create mode 100644 test/20insert.rb create mode 100644 test/30select.rb create mode 100644 test/40update.rb create mode 100644 test/45delete.rb create mode 100644 test/50drop_table.rb create mode 100644 test/70close.rb create mode 100644 test/test.rb create mode 100644 test/utf8/test.rb diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..30898e1 --- /dev/null +++ b/COPYING @@ -0,0 +1,53 @@ +Ruby/ODBC is copyrighted free software by Christian Werner chw at ch-werner.de +You can redistribute it and/or modify it under either the terms of the GPL +(see the file GPL), or the conditions below: + + 1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + + 2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b) use the modified software only within your corporation or + organization. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a) distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b) accompany the distribution with the machine-readable source of + the software. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..a8cad29 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,350 @@ +ODBC binding for Ruby +--------------------- + +Sat Dec 26 2020 version 0.999991 released + + * update to compile with newer Ruby releases + * report larger width for SQL_(DECIMAL|NUMERIC) columns + +Wed Feb 28 2018 version 0.99999 released + + * update to compile with newer Ruby releases, thanks + Lars Kanis for patch + * added ODBC::Database.login_timeout to get/set the + SQL_LOGIN_TIMEOUT connection attribute + +Wed Feb 15 2017 version 0.99998 released + + * minor update to compile with Ruby 2.4, thangs to Kevin Deisz + * preset output vars before SQLColAttributes() call + +Wed Apr 15 2015 version 0.99997 released + + * fixed bug (typo) when compiling with Ruby < 2.0 + +Wed Mar 25 2015 version 0.99996 released + + * when built for Ruby >= 2.0 release GVL for potentially + long running ODBC API functions, thanks Matt Conover + for initial implementation + +Wed Mar 13 2013 version 0.99995 released + + * added ODBC::Database.use_sql_column_name= flag to switch + usage of SQLColAttributes(SQL_COLUMN_LABEL) to + SQLColAttributes(SQL_COLUMN_NAME) on per connection basis + +Sat Jan 15 2011 version 0.99994 released + + * fixed column key caching, thanks Sean Noonan for bug report + +Fri Dec 24 2010 version 0.99993 released + + * fixed error reporting in Database.transaction method, + thanks Shane Emmons for bug report and testing + +Thu Sep 16 2010 version 0.99992 released + + * ODBC::Statement.each/each_hash now output arrays when + invoked without block + * column keys in result hashes now are cached/recycled Ruby strings + * added ODBC::Database methods use_time/use_utc to directly + output Ruby Time/Date objects + * added encoding support in the UTF8 variant for Ruby >= 1.9 + * added module constant ODBC::UTF8 to indicate variant of module + * fixes for M$SQL server reporting zero column sizes + and unknown data types for varchar(strmax) columns + * eliminated compiler warnings + * use StringValueCStr instead of STR2CSTR (Ruby >= 1.9.1) + * small change for decision when to use SQLGetData() loop + +Sun Apr 25 2010 version 0.99991 released + + * detect HAVE_LONG_LONG for builds with unixODBC + * UTF-8 coding/decoding fix for range 0x80..0xBF + +Thu Feb 18 2010 version 0.9999 released + + * integrated Bosko Ivanisevic's fixes in ext/utf8/extconf.rb + and test/test.rb and test/utf8/test.rb + +Fri Jan 15 2010 version 0.9998 released + + * documentation update + * reorganized directory layout for making a gem + +Wed May 20 2009 version 0.9997 released + + * added Chad Johnson's FreeTDS/MSSQL patch to prevent from + re-prepare of queries with SQL_VARCHAR parameters + +Mon Feb 02 2009 version 0.9996 released + + * updates for Ruby > 1.8 + * added Stephen Veit's SQL_NO_TOTAL handling to improve + retrieving large SQL_C_BINARY data + * fixes for SQLExecute/SQLExecDirect returning SQL_NO_DATA + +Sat Apr 07 2007 version 0.9995 released + + * init.c: prefer libiodbc.so.2 over libiodbc.so and + libodbc.so.1 over libodbc.so and same logic for + the installer shared library + * no exception thrown on stale HSTMT for ODBC::Statement.nrows + +Mon Dec 25 2006 version 0.9994 released + + * improved doc on ODBC::Statement.*param*, ODBC::Parameter, + and related ODBCProc + * added support for output/input-output parameters in queries, + methods ODBC::Statement.param_iotype/param_output_type/ + param_output_size/param_output_value + +Fri Sep 15 2006 version 0.9993 released + + * more tweaks in extconf.rb for --enable-dlopen'ed utf8 version + thanks Daigo Moriwaki for testing + * eliminated warnings for installer functions + +Mon Sep 11 2006 version 0.9992 released + + * added parameter "column" in ODBC::Database::columns + * intern most strings on module load + * enhanced ODBC::Statement.fetch_hash et.al. to produce + intern'ed strings or fixnums as hash keys as requested + by Neville Burnell + * handle SQL_NO_DATA for chunk-wise SQLGetData() + * determine dynamically which API (UNICODE or ANSI) to + use for ODBC installer functions + * more 64 bit fixes + * added missing SQLEndTran() in init.c + +Sat Aug 05 2006 version 0.9991 released + + * fixed more SQLLEN vs SQLINTEGER issues and 64 bit warnings + * bail out during extconf.rb when sql.h/sqlext.h is not + available as requested by Curt Sampson + * fixed transaction logic ENV vs. DBC and use SQLEndTran() + for ODBCVER >= 0x0300, thanks Adam Harper for patch + * detect SQL parameter strings containing '\0's and use + SQL_C_BINARY when appropriate as parameter types to support + BLOB data, thanks Daigo Moriwaki for report and testing + * don't use dlopen()/GCC weak attributes on MacOSX + +Wed Jun 28 2006 version 0.999 released + + * added ODBC::Column@autoincrement information + * added init.c and extconf.rb logic for resolving iODBC/unixODBC + library dependencies at runtime using GCC weak attribute in + conjunction with dlopen(), this feature can be enabled/disabled + with "ruby extconf.rb --enable-dlopen" + +Sun May 21 2006 version 0.998 released + + * added undocumented module methods ODBC::write_file_dsn and + ODBC::read_file_dsn + * fixes in SQLConfigDataSource/SQLInstallerError detect/usage + * added statement level option handling (SQLSetStmtOption), + ODBC::Database.newstmt, ODBC::Statement.prepare|run as + contributed by Carl Blakeley of OpenLink Software + * eliminated most GCC 4.x warnings + +Sun Dec 25 2005 version 0.997 released + + * 32/64 bit issues as pointed out by Lars Kanis + * ODBC::Database.get_info implementation and constants, idea + and initial coding contributed by Tim Haynes of OpenLink Software + * ODBC::Statement.param_type implemented as work around for + Ingres ODBC driver. Thanks Jon Chambers for idea and testing + +Sun Nov 21 2004 version 0.996 released + + * use SQLInstallerError() for adddsn/confdsn/deldsn if available, + code contributed by Ryszard Niewisiewicz + * retry SQLBindParameter() for SQL_VARCHAR when SQLDescribeParam() + doesn't work (e.g. on MS Jet Engine) + * fixed crashes when SQLBindParameter failed in stmt_exec_int(), + as reported by Ryszard Niewisiewicz + +Tue Sep 07 2004 version 0.995 released + + * fixed another buffer alignment bug fetching doubles on sparc, + thanks Dan Debertin for testing. + +Wed Jul 14 2004 version 0.994 released + + * fixed ODBC::Database.new|connect so that + ODBC::Database.new.drvconnect(string) works + * fixed SQLTCHAR detect for Win32 in extconf.rb, + thanks to Pedro Luis Castedo Cepeda + +Mon Jun 28 2004 version 0.993 released + + * use HAVE_TYPE_SQLTCHAR from extconf.rb for old iODBC versions + * don't rb_define_alloc_func of dsn_new/drv_new for Ruby >= 1.8, + gives stack overflows, thanks to Pedro Luis Castedo Cepeda + for fixing + * fixes for iODBC driver manager concerning SQLFetchScroll + availability (SQL state codes IM001 vs HYC000) + * blob handling fix as suggested by Kaspar Schiess + +Mon Mar 22 2004 version 0.992 released + + * fixed buggy UTF8->Unicode conversion code + * fixed handling of uppercase column names in row hashes + +Fri Dec 26 2003 version 0.991 released + + * use SQLConfigDataSourceW() in utf8 version + * SQLError() is now always called after error/warning from + other ODBC API invokations in order to cleanup error + messages in driver manager/driver + * tweaks for SQLColAttributes() problems as suggested + by Nathaniel Talbott + * modified extconf.rb as suggestedb by + * eliminated use of alloca() in utf8 converters + * reworked to use Ruby 1.8 rb_define_alloc_func() API + +Wed May 21 2003 version 0.99 released + + * no functional changes with respect to 0.98 + but starting with 0.99 released under Ruby style license. + +Thu May 08 2003 version 0.98 released + + * added utf8 version (-DUNICODE, -D_UNICODE for CFLAGS), + which generates odbc_utf.so, for build instructions + see README. Thanks Bosko Ivanisevic + for ideas and testing. + +Fri Apr 25 2003 version 0.97 released + + * align buffers for fetching scalar data (long, double et.al.) + to prevent alignment problems causing bus errors e.g. on sparc, + thanks sakazuki for testing. + +Wed Mar 19 2003 version 0.96 released + + * added fall back to SQLFetch() when SQLFetchScroll(SQL_FETCH_NEXT) + or SQLExtendedFetch(SQL_FETCH_NEXT) report IM001 error, + thanks Kiyonori Takeda for testing. + +Mon May 13 2002 version 0.95 released + + * doc updated + * minor code cleanups + * fixed ODBC::Database.run to properly accept blocks + as reported by Michael Neumann + * added ODBC::Statement.more_results method as + requested by Chris Morris + +Thu Oct 11 2001 version 0.94 released + + * doc updated + * fixed off-by-one bug in do_fetch function + * added some more SQL_* constants + * added ODBC::Database.special_columns method + * added unique flag to ODBC::Database.indexes + +Fri Sep 07 2001 version 0.93 released + + * doc updated + * removed default column name conversion to upper case + as introduced in version 0.91 + * added ignorecase, ignorecase= methods for ODBC::Database + and ODBC::Statement as suggested by + Sean O'Halpin + +Fri Aug 31 2001 version 0.92 released + + * ODBC::Parameter class and ODBC::Statement.make_proc method added + * changes to support libiodbc-2.50 eg on Debian, thanks + Massimiliano Mirra for testing + * fixed typos in constant table (SQL_CP_* symbols, O_CONSTU macro) + * if odbcinst.h not available, make dummy DSN config methods + +Sat Aug 25 2001 version 0.91 released + + * handle non-unique keys in column name/result set hashes + by appending "#column-number" string + * convert column names to uppercase if ODBC driver is + identifier case insensitive as suggested by + Elmar Sonnenschein + * added cursorname method in Statement + * added assignment methods for Environment/Database/Statement + options + * taint strings + * use SQLExecDirect() in Statement.run/do when no parameters in query + +Wed Aug 15 2001 version 0.9 released + + * doc updated + * added alloc/free and ODBC API call tracing + (compile-time, -DTRACING) + * added array result for ODBC::Statement.columns + * statements are now by default dropped before SQLDisconnect + * rewrote resource tracking of environment, database connection + and statement objects due to crashes/hangs on Win32 + * improved constructors for data/time/timestamp objects + * added clone methods for date/time/timestamp objects + +Thu Jul 26 2001 version 0.8 released + + * minor doc update + * added handling of block for ODBC::Statement.columns method + * revised error reporting and SQLRETURN handling with + respect to SQL_SUCCESS_WITH_INFO, introduced info method + to get warnings of last ODBC API call. Change was required + to support MS SQL Server, thanks to correspondence with + David Moore . + +Wed Jun 20 2001 version 0.7 released + + * minor doc update + * some more test cases in fetching data + * added some more fetch methods (fetch_many, fetch_all ...) + * added more tests for conditional include of + * again, fixes in parameter binding thanks to patches from + Steve Tuckner + +Tue Jun 12 2001 version 0.6 released + + * reviewed parameter and column information handling + * work around cases when SQLDescribeParam is not supported + by the selected ODBC driver + * bug fixes in parameter binding + +Sun Jun 10 2001 version 0.5 released + + * doc updated + * added environment attribute setting/getting methods + * eliminated alloca() in fetching data + * added missing SQL_CHAR constant + * fixed accessor methods for ODBC::(DSN|Driver|Column) as + suggested by Michael Neumann + +Fri Jun 08 2001 version 0.4 released + + * Cygwin fixes thanks to Michael Neumann + * minor optimizations + * cleanup GC related functions + +Sun May 27 2001 version 0.3 released + + * doc updated + * win32 port (mswin32 working, mingw untested) + * added types (ODBC::Date, ODBC::Time, ODBC::TimeStamp) + * reworked module/class organization + * major review + +Thu May 17 2001 version 0.2 released + + * updated and improved doc + * added fetch_hash, each_hash, column ODBCStmt methods + * added more constants in ODBC class + * added ODBCColumn class + * added accessor for ODBC::error + +Sun May 13 2001 version 0.1 released + + * initial release. diff --git a/GPL b/GPL new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..21b835a --- /dev/null +++ b/MANIFEST @@ -0,0 +1,23 @@ +COPYING +ChangeLog +GPL +MANIFEST +README +ruby-odbc.gemspec +ext/extconf.rb +ext/odbc.c +ext/init.c +ext/utf8/extconf.rb +ext/utf8/odbc.c +ext/utf8/init.c +lib/cqgen.rb +doc/odbc.html +test/test.rb +test/00connect.rb +test/10create_table.rb +test/20insert.rb +test/30select.rb +test/40update.rb +test/50drop_table.rb +test/70close.rb +test/utf8/test.rb diff --git a/README b/README new file mode 100644 index 0000000..3a500f9 --- /dev/null +++ b/README @@ -0,0 +1,114 @@ +# $Id: README,v 1.47 2020/12/25 21:47:43 chw Exp chw $ + +ruby-odbc-0.999991 + +This is an ODBC binding for Ruby. So far it has been tested with + + - Ruby 1.[6-9], MySQL 3.22/MyODBC (local), unixODBC 2.1.0 + on Linux 2.2-x86 and 2.6-x86_64 + + - Ruby 1.6.4, MySQL 3.22/MyODBC (local), libiodbc 2.50 + on Linux 2.2-x86 + + - Ruby 1.[6-8], MySQL 3.22/MyODBC (remote), MS Jet Engine, MSVC++ 6.0 + on Windows NT4SP6 + + - Ruby 1.6.[3-5], MySQL 3.22/MyODBC (remote), MS Jet Engine, cygwin, + on Windows NT4SP6 and 2000 + + - Ruby 1.8.*, SQLite/ODBC >= 0.67, libiodbc 3.52.4 on Fedora Core 3 x86 + + - Ruby 2.0.0, SQLite/ODBC >= 0.93, unixODBC 2.2.14 on Ubuntu 12.04 x86 + + - Ruby >= 2.4 + + Michael Neumann and + Will Merrell reported successful + compilation with Cygwin on Win32. + +Requirements: + + - Ruby 1.6.[3-8] or Ruby >= 1.7 + - unixODBC 2.x or libiodbc 3.52 on UN*X + +Installation: + + $ ruby -Cext extconf.rb [--enable-dlopen|--disable-dlopen] + $ make -C ext + # make -C ext install + + --enable/disble-dlopen turns on/off special initialization + code to make ruby-odbc agnostic to unixODBC/iODBC driver + manager shared library names when GCC is used for compile. + In cases where unixODBC or iODBC is installed in non-standard + locations, use the option --with-odbc-dir= + when running extconf.rb + +Installation of utf8 version: + + $ ruby -Cext/utf8 extconf.rb [--enable-dlopen|--disable-dlopen] + $ make -C ext/utf8 + # make -C ext/utf8 install + +Installation MSVC: + + C:..>ruby -Cext extconf.rb + C:..>cd ext + C:..>nmake + C:..>nmake install + C:..>ruby -Cutf8 extconf.rb + C:..>cd utf8 + C:..>nmake + C:..>nmake install + +Testing: + + $ ruby -Ctest test.rb DSN [uid] [pwd] + or + $ ruby -KU -Ctest/utf8 test.rb DSN [uid] [pwd] + +Usage: + + Refer to doc/odbc.html + + The difference between utf8 and non-utf8 versions are: + + - non-utf8 version uses normal SQL.* ANSI functions + - utf8 version uses SQL.*W UNICODE functions and + requires/returns all strings in UTF8 format + + Thus, depending on the -K option of ruby one could use + that code snippet: + + ... + if $KCODE == "UTF8" then + require 'odbc_utf8' + else + require 'odbc' + fi + + It is also possible to load both non-utf8 and utf8 version + into ruby: + + ... + # non-utf8 version + require 'odbc' + # utf8 version + require 'odbc_utf8' + + Whichever is loaded first, gets the module name 'ODBC'. + The second loaded module will be named 'ODBC_UTF8' (for + 'odbc_utf8') or 'ODBC_NONE' (for 'odbc'). That should + allow to use both versions simultaneously in special + situations. + +TODO: + + - heavier testing + - improve documentation + +Author: + + Christian Werner + mailto:chw @nospam@ ch-werner.de + http://www.ch-werner.de/rubyodbc diff --git a/doc/odbc.html b/doc/odbc.html new file mode 100644 index 0000000..d66f349 --- /dev/null +++ b/doc/odbc.html @@ -0,0 +1,1353 @@ + + + + + + + + + Ruby ODBC Reference + + +

Ruby ODBC Reference

+
+ Last update: Wed, 28 February 2018 +
+
+
+

ODBC

+

+ The module to encapsulate the Ruby ODBC binding. +

+

module functions:

+
+
datasources +
Returns an array of ODBC::DSNs, i.e. all + known data source names. +
drivers +
Returns an array of ODBC::Drivers, + i.e. all known ODBC drivers. +
error +
Returns the last error messages (String array) or nil. + The layout of the warning/error messages + is described here. + Retrieving this message as well as subsequent succeeding ODBC + method invocations do not clear the message. Use the + clear_error method + for that purpose. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + The layout of the warning/error messages + is described here. + Retrieving this message as well as subsequent succeeding ODBC + method invocations do not clear the message. Use the + clear_error method + for that purpose. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) +
Raises an ODBC::Error exception with + String error message value. +
newenv +
Returns a new ODBC::Environment. +
+ connection_pooling[=value] +
Gets or sets the process-wide connection pooling attribute. +
+ to_time(timestamp) +
+ to_time(date,[time]) +
+ to_time(time,[date]) +
Creates a Time object from the specified arguments, + which must be ODBC::Date, + ODBC::Time, or + ODBC::TimeStamp objects. +
+ to_date(timestamp) +
to_date(date) +
Creates a Date object from the specified arguments, + which must be ODBC::Date, or + ODBC::TimeStamp objects. +
connect(dsn,[user,passwd]) + [{|dbc| block}] +
If no block is specified, a connection to the given data source + is established and a ODBC::Database + object is returned, identifying that connection. Otherwise, + the block is executed with the database object. When the block + is finished, the connection is automatically released. + Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
+

constants:

+

+ The boolean constant UTF8 reports the character encoding + of the module. If it is true, the UTF-8 variant of + the module is in use, and string data is automatically converted + to/from Unicode. +

+

+ Some constants of the ODBC API are defined in order to set connection + options, to deal with SQL data types, and to obtain database meta data. +

+
+
Cursor behaviour: +
SQL_CURSOR_FORWARD_ONLY, + SQL_CURSOR_KEYSET_DRIVEN, + SQL_CURSOR_DYNAMIC, + SQL_CURSOR_STATIC +
Concurrency (transactions): +
SQL_CONCUR_READ_ONLY, + SQL_CONCUR_LOCK, + SQL_CONCUR_ROWVER, + SQL_CONCUR_VALUES +
Fetch direction: +
SQL_FETCH_NEXT, + SQL_FETCH_FIRST, + SQL_FETCH_LAST, + SQL_FETCH_PRIOR, + SQL_FETCH_ABSOLUTE, + SQL_FETCH_RELATIVE +
Data types: +
SQL_UNKNOWN_TYPE, + SQL_CHAR, + SQL_NUMERIC, + SQL_DECIMAL, + SQL_INTEGER, + SQL_SMALLINT, + SQL_FLOAT, + SQL_REAL, + SQL_DOUBLE, + SQL_VARCHAR, + SQL_DATETIME, + SQL_DATE, + SQL_TYPE_DATE, + SQL_TIME, + SQL_TYPE_TIME, + SQL_TIMESTAMP, + SQL_TYPE_TIMESTAMP, + SQL_LONGVARCHAR, + SQL_BINARY, + SQL_VARBINARY, + SQL_LONGVARBINARY, + SQL_BIGINT, + SQL_TINYINT, + SQL_BIT, + SQL_GUID +
Parameter related: +
SQL_PARAM_TYPE_UNKNOWN, + SQL_PARAM_INPUT, + SQL_PARAM_OUTPUT, + SQL_PARAM_INPUT_OUTPUT, + SQL_DEFAULT_PARAM + SQL_RETURN_VALUE +
Procedure related: +
SQL_RESULT_COL, + SQL_PT_UNKNOWN, + SQL_PT_PROCEDURE, + SQL_PT_FUNCTION +
Environment attributes: +
SQL_CP_OFF, + SQL_CP_ONE_PER_DRIVER, + SQL_CP_ONE_PER_HENV, + SQL_CP_DEFAULT, + SQL_CP_STRICT_MATCH, + SQL_CP_RELAXED_MATCH, + SQL_CP_MATCH_DEFAULT, + SQL_OV_ODBC2, + SQL_OV_ODBC3 +
Info types for + ODBC::Database.get_info + yielding integer results: +
SQL_ACTIVE_ENVIRONMENTS, + SQL_ACTIVE_CONNECTIONS, + SQL_ACTIVE_STATEMENTS, + SQL_ASYNC_MODE, + SQL_CATALOG_LOCATION, + SQL_CONCAT_NULL_BEHAVIOR, + SQL_CORRELATION_NAME, + SQL_CURSOR_COMMIT_BEHAVIOR, + SQL_CURSOR_ROLLBACK_BEHAVIOR, + SQL_CURSOR_SENSITIVITY, + SQL_DDL_INDEX, + SQL_DEFAULT_TXN_ISOLATION, + SQL_DRIVER_HDBC, + SQL_DRIVER_HENV, + SQL_DRIVER_HDESC, + SQL_DRIVER_HLIB, + SQL_DRIVER_HSTMT, + SQL_FILE_USAGE, + SQL_GROUP_BY, + SQL_IDENTIFIER_CASE, + SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, + SQL_MAX_BINARY_LITERAL_LEN, + SQL_MAX_CATALOG_NAME_LEN, + SQL_MAX_CHAR_LITERAL_LEN, + SQL_MAX_COLUMN_NAME_LEN, + SQL_MAX_COLUMNS_IN_GROUP_BY, + SQL_MAX_COLUMNS_IN_INDEX, + SQL_MAX_COLUMNS_IN_ORDER_BY, + SQL_MAX_COLUMNS_IN_SELECT, + SQL_MAX_COLUMNS_IN_TABLE, + SQL_MAX_CONCURRENT_ACTIVITIES, + SQL_MAX_CURSOR_NAME_LEN, + SQL_MAX_DRIVER_CONNECTIONS, + SQL_MAX_IDENTIFIER_LEN, + SQL_MAX_INDEX_SIZE, + SQL_MAX_OWNER_NAME_LEN, + SQL_MAX_PROCEDURE_NAME_LEN, + SQL_MAX_QUALIFIER_NAME_LEN, + SQL_MAX_ROW_SIZE, + SQL_MAX_SCHEMA_NAME_LEN, + SQL_MAX_STATEMENT_LEN, + SQL_MAX_TABLE_NAME_LEN, + SQL_MAX_TABLES_IN_SELECT, + SQL_MAX_USER_NAME_LEN, + SQL_NON_NULLABLE_COLUMNS, + SQL_NULL_COLLATION, + SQL_ODBC_API_CONFORMANCE, + SQL_ODBC_INTERFACE_CONFORMANCE, + SQL_ODBC_SAG_CLI_CONFORMANCE, + SQL_ODBC_SQL_CONFORMANCE, + SQL_PARAM_ARRAY_ROW_COUNTS, + SQL_PARAM_ARRAY_SELECTS, + SQL_QUALIFIER_LOCATION, + SQL_QUOTED_IDENTIFIER_CASE, + SQL_SQL_CONFORMANCE, + SQL_TXN_CAPABLE +
Info types for + ODBC::Database.get_info + yielding bitmasks (integer results): +
SQL_AGGREGATE_FUNCTIONS, + SQL_ALTER_DOMAIN, + SQL_ALTER_TABLE, + SQL_BATCH_ROW_COUNT, + SQL_BATCH_SUPPORT, + SQL_BOOKMARK_PERSISTENCE, + SQL_CATALOG_USAGE, + SQL_CONVERT_BINARY, + SQL_CONVERT_BIT, + SQL_CONVERT_CHAR, + SQL_CONVERT_GUID, + SQL_CONVERT_DATE, + SQL_CONVERT_DECIMAL, + SQL_CONVERT_DOUBLE, + SQL_CONVERT_FLOAT, + SQL_CONVERT_FUNCTIONS, + SQL_CONVERT_INTEGER, + SQL_CONVERT_INTERVAL_YEAR_MONTH, + SQL_CONVERT_INTERVAL_DAY_TIME, + SQL_CONVERT_LONGVARBINARY, + SQL_CONVERT_LONGVARCHAR, + SQL_CONVERT_NUMERIC, + SQL_CONVERT_REAL, + SQL_CONVERT_SMALLINT, + SQL_CONVERT_TIME, + SQL_CONVERT_TIMESTAMP, + SQL_CONVERT_TINYINT, + SQL_CONVERT_VARBINARY, + SQL_CONVERT_VARCHAR, + SQL_CONVERT_WCHAR, + SQL_CONVERT_WLONGVARCHAR, + SQL_CONVERT_WVARCHAR, + SQL_CREATE_ASSERTION, + SQL_CREATE_CHARACTER_SET, + SQL_CREATE_COLLATION, + SQL_CREATE_DOMAIN, + SQL_CREATE_SCHEMA, + SQL_CREATE_TABLE, + SQL_CREATE_TRANSLATION, + SQL_CREATE_VIEW, + SQL_DATETIME_LITERALS, + SQL_DROP_ASSERTION, + SQL_DROP_CHARACTER_SET, + SQL_DROP_COLLATION, + SQL_DROP_DOMAIN, + SQL_DROP_SCHEMA, + SQL_DROP_TABLE, + SQL_DROP_TRANSLATION, + SQL_DROP_VIEW, + SQL_DTC_TRANSITION_COST, + SQL_DYNAMIC_CURSOR_ATTRIBUTES1, + SQL_DYNAMIC_CURSOR_ATTRIBUTES2, + SQL_FETCH_DIRECTION, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + SQL_GETDATA_EXTENSIONS, + SQL_KEYSET_CURSOR_ATTRIBUTES1, + SQL_KEYSET_CURSOR_ATTRIBUTES2, + SQL_INDEX_KEYWORDS, + SQL_INFO_SCHEMA_VIEWS, + SQL_INSERT_STATEMENT, + SQL_LOCK_TYPES, + SQL_NUMERIC_FUNCTIONS, + SQL_OJ_CAPABILITIES, + SQL_OWNER_USAGE, + SQL_POS_OPERATIONS, + SQL_POSITIONED_STATEMENTS, + SQL_QUALIFIER_USAGE, + SQL_SCHEMA_USAGE, + SQL_SCROLL_CONCURRENCY, + SQL_SCROLL_OPTIONS, + SQL_SQL92_DATETIME_FUNCTIONS, + SQL_SQL92_FOREIGN_KEY_DELETE_RULE, + SQL_SQL92_FOREIGN_KEY_UPDATE_RULE, + SQL_SQL92_GRANT, + SQL_SQL92_NUMERIC_VALUE_FUNCTIONS, + SQL_SQL92_PREDICATES, + SQL_SQL92_RELATIONAL_JOIN_OPERATORS, + SQL_SQL92_REVOKE, + SQL_SQL92_ROW_VALUE_CONSTRUCTOR, + SQL_SQL92_STRING_FUNCTIONS, + SQL_SQL92_VALUE_EXPRESSIONS, + SQL_STANDARD_CLI_CONFORMANCE, + SQL_STATIC_CURSOR_ATTRIBUTES1, + SQL_STATIC_CURSOR_ATTRIBUTES2, + SQL_STATIC_SENSITIVITY, + SQL_STRING_FUNCTIONS, + SQL_SUBQUERIES, + SQL_SYSTEM_FUNCTIONS, + SQL_TIMEDATE_ADD_INTERVALS, + SQL_TIMEDATE_DIFF_INTERVALS, + SQL_TIMEDATE_FUNCTIONS, + SQL_TXN_ISOLATION_OPTION, + SQL_UNION +
Info types for + ODBC::Database.get_info + yielding strings: +
SQL_ACCESSIBLE_PROCEDURES, + SQL_ACCESSIBLE_TABLES, + SQL_CATALOG_NAME, + SQL_CATALOG_NAME_SEPARATOR, + SQL_CATALOG_TERM, + SQL_COLLATION_SEQ, + SQL_COLUMN_ALIAS, + SQL_DATA_SOURCE_NAME, + SQL_DATA_SOURCE_READ_ONLY, + SQL_DATABASE_NAME, + SQL_DBMS_NAME, + SQL_DBMS_VER, + SQL_DESCRIBE_PARAMETER, + SQL_DM_VER, + SQL_DRIVER_NAME, + SQL_DRIVER_ODBC_VER, + SQL_DRIVER_VER, + SQL_EXPRESSIONS_IN_ORDERBY, + SQL_IDENTIFIER_QUOTE_CHAR, + SQL_INTEGRITY, + SQL_KEYWORDS, + SQL_LIKE_ESCAPE_CLAUSE, + SQL_MAX_ROW_SIZE_INCLUDES_LONG, + SQL_MULT_RESULT_SETS, + SQL_MULTIPLE_ACTIVE_TXN, + SQL_NEED_LONG_DATA_LEN, + SQL_ODBC_SQL_OPT_IEF, + SQL_ODBC_VER, + SQL_ORDER_BY_COLUMNS_IN_SELECT, + SQL_OUTER_JOINS, + SQL_OWNER_TERM, + SQL_PROCEDURE_TERM, + SQL_PROCEDURES, + SQL_QUALIFIER_NAME_SEPARATOR, + SQL_QUALIFIER_TERM, + SQL_ROW_UPDATES, + SQL_SCHEMA_TERM, + SQL_SEARCH_PATTERN_ESCAPE, + SQL_SERVER_NAME, + SQL_SPECIAL_CHARACTERS, + SQL_TABLE_TERM, + SQL_USER_NAME, + SQL_XOPEN_CLI_YEAR +
Options for + ODBC::Database.get_option, + ODBC::Database.set_option, + ODBC::Statement.get_option, + and + ODBC::Statement.set_option + yielding integers: +
SQL_AUTOCOMMIT, + SQL_LOGIN_TIMEOUT, + SQL_CONCURRENCY, + SQL_QUERY_TIMEOUT, + SQL_MAX_ROWS, + SQL_MAX_LENGTH, + SQL_NOSCAN, + SQL_ROWSET_SIZE, + SQL_CURSOR_TYPE +
+
+
+
+

ODBC::Object

+

+ The class to represent the root of all other ODBC related objects. +

+

super class:

+ Object +

methods:

+
+
error +
Returns the last error message (String) or nil. For further + information see ODBC::error. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + For further information see + ODBC::error. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) + +
Raises an ODBC::Error exception with + String error message value. +
+

singleton methods:

+
+
error +
Returns the last error message (String array) or nil. + For further information see + ODBC::error. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + For further information see + ODBC::error. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) + +
Raises an ODBC::Error exception with + String error message value. +
+
+
+
+

ODBC::Environment

+

+ The class to represent the environment for + ODBC::Databases. +

+

super class:

+ ODBC::Object +

methods:

+
+
connect(dsn,[user,passwd]) +
Connects to an ODBC data source and returns a + ODBC::Database object. + Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
environment + +
Returns the ODBC::Environment + of the object. +
commit +
Commits the current transaction. +
rollback +
Rollbacks the current transaction. +
+ transaction {|env| block} +
First commits the current transaction, then executes the given + block where the paramater is the object itself (an environment or + a database connection). If the block raises an exception, the + transaction is rolled back, otherwise committed. +
+ connection_pooling[=value] +
Gets or sets the connection pooling attribute of the environment. +
+ cp_match[=value] +
Gets or sets the connection pooling match attribute of the + environment. +
+ odbc_version[=value] +
Gets or sets the ODBC version attribute of the environment. +
+

singleton methods:

+
+
new +
Returns a new ODBC::Environment + object. +
+
+
+
+

ODBC::Database

+

+ The class to represent a connection to an ODBC data source. +

+

+ When underlying ODBC SQL functions report an error, + an ODBC::Error + exception with a corresponding error message from the ODBC + driver manager and/or driver is raised. +

+

super class:

+ ODBC::Environment +

methods:

+
+
connected? +
Returns true when the object is in connected + state, false otherwise. +
drvconnect(drv) +
Connect to a data source specified by drv + (ODBC::Driver). +
+ disconnect(no_drop=false) +
Disconnects from the data source, all active statements for the + connection are dropped except when the no_drop argument + is true. The method returns true when the connection was released, + false otherwise. +
newstmt +
Returns a new ODBC::Statement + object without preparing or executing a SQL statement. This allows + to use + ODBC::Statement.set_option + to set special options on the statement before actual execution. +
tables([pattern]) +
Returns an ODBC::Statement with + information of all or some (pattern String given) + tables of the data source. +
columns([table[,column]]) +
Returns an ODBC::Statement + with column information + of the given table (String) and optional + column (String). +
+ indexes([table[,unique]]) +
Returns an ODBC::Statement + with index information + of the given table (String). If unique + is given and true, information for unique indexes only is + returned. +
types([tcode]) +
Returns an ODBC::Statement + with type information + of the numeric SQL type tcode or all types if + tcode is omitted. +
+ primary_keys([table]) +
Returns an ODBC::Statement + with primary key information of the given table (String). +
+ foreign_keys([table]) +
Returns an ODBC::Statement + with foreign key information of the given table (String). +
+ table_privileges([table]) +
Returns an ODBC::Statement + with owner/right information of the given table (String). +
procedures([p]) +
Returns an ODBC::Statement + with information about stored procedures. +
+ procedure_columns([p]) +
Returns an ODBC::Statement + with information about stored procedures. +
+ special_columns([table[,id,scope]]) +
Returns an ODBC::Statement + with column information + of the given table (String) indicating the + optimal unique or identifying columns. + id may + be specified as ODBC::SQL_BEST_ROWID or + ODBC::SQL_ROW_VER. scope may be + specified as ODBC::SQL_SCOPE_CURROW, + ODBC::SQL_SCOPE_TRANSACTION, or + ODBC::SQL_SCOPE_SESSION. If omitted the + defaults are ODBC::SQL_BEST_ROWID and + ODBC::SQL_SCOPE_CURROW. +
+ get_info(info_type[,sql_type]) +
Retrieves database meta data according to info_type + and returns numbers or strings. info_type may be specified + as integer or string. To force interpretation of the return value + of the underlying ODBC function SQLGetInfo() as a + specific Ruby type the optional parameter sql_type + can be given as e.g. + ODBC::SQL_SMALLINT (yielding integer), + ODBC::SQL_INTEGER (yielding integer), and + ODBC::SQL_CHAR (yielding string). +
run(sql[,args...]) +
Prepares and executes the query specified by sql + with parameters bound from args and returns an + ODBC::Statement or nil, if + the query did not produce a result set, e.g. when executing + SQL insert, delete, or update statements. +
do(sql[,args...]) +
Prepares and executes the query as in + run, + but returns the number of result rows and automatically drops + the statement. Useful for SQL insert, delete, or update statements. +
prepare(sql) +
Prepares the query specified by sql and returns + an ODBC::Statement. +
proc(sql,[type,size[,n=1]]) + {|stmt| block} +
Prepares the query specified by sql within a + ODBCProc and returns that procedure. + When the procedure is called, the statement is executed with + the procedure's arguments bound to the statement's parameters + and the statement is bound as parameter in the block, e.g. +
# add customer given name and id
+# and return number of affected rows
+
+addcust = dbc.proc("insert into customer values(?, ?)") { |stmt|
+    stmt.nrows
+}
+
+addcust.call("J.R. User", 9999)
+
+# alternative to call: addcust["J.R. User", 9999]
+ The optional arguments type, size, and + n, + make the n-th statement parameter into an output parameter + with that given ODBC data type and size. That statement + parameter must be omitted when the ODBCProc + is called. This can be useful for wrapping stored procedures, e.g. +
# wrap statement with output parameter
+
+aproc = dbc.proc("{ ? = call stored_proc(?) }", ODBC::SQL_INTEGER, 4) {}
+
+# call this statement/procedure, output parameter is result
+
+result = aproc.call(99)
+
+# alternative to call: result = aproc[99]
+ Finalization of an ODBCProc can be + done according to this code snippet +
# aproc is wrapped statement, finalize it now
+
+aproc.statement.drop
+
+ get_option(option) +
Gets a connection level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. This method returns the integer value of that option. +
+ set_option(option,intval) +
Sets a connection level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. The second parameter intval can be used to + set driver-specific statement options. +
autocommit[=bool] +
Sets or queries the autocommit option of the connection. +
+ concurrency[=intval] +
Sets or queries the concurrency mode of cursors opened on + the connection. +
maxrows[=intval] +
Sets or queries the maximum rows returned in result sets from + the connection. +
timeout[=intval] +
Sets or queries the number of seconds to wait for queries to + execute on the connection before returning. +
+ login_timeout[=intval] +
Sets or queries the number of seconds to wait for a login to + complete on the connection before returning. +
maxlength[=intval] +
Sets or queries the maximum amount of data that will be returned + from a character or binary column. +
+ cursortype[=intval] +
Sets or queries the cursor type used for fetches. +
noscan[=bool] +
Sets or queries whether the driver scans SQL strings for ODBC + escape clauses. +
rowsetsize +
Returns the number of rows in a rowset fetched internally by a + call o SQLExtendedFetch (hardcoded to 1). +
ignorecase[=bool] +
Sets or queries the uppercase conversion for column names. If + turned on (bool is true), + ODBC::Statements + created by database methods will report column names in + ODBC::Column + or in the statement fetch methods + as uppercase strings. Otherwise (the default) column names are + passed unmodified. +
drop_all +
Releases the resources of all open + ODBC::Statements in this + database connection. +
use_time[=bool] +
Sets or queries the mapping of SQL_DATE, SQL_TIME, and + SQL_TIMESTAMP data types to Ruby objects. When true, + SQL_DATE is mapped to Ruby Date objects, SQL_TIME and + SQL_TIMESTAMP are mapped to Ruby Time objects. Otherwise (default) + ODBC::Date, + ODBC::Time, and + ODBC::TimeStamp are used. +
use_utc[=bool] +
Sets or queries the timezone applied on SQL_DATE, SQL_TIME, and + SQL_TIMESTAMP data types to Ruby objects. When true, + Ruby Date and Time objects are represented in UTC, when + false (default) in the local timezone. +
+ use_sql_column_name[=bool] +
Sets or queries the flag controlling how column names are + read from the data source. When false (default), the ODBC + API SQLColAttributes(SQL_COLUMN_LABEL) is used, + otherwise SQLColAttributes(SQL_COLUMN_NAME) is used. +
+

singleton methods:

+
+
new +
Creates an unconnected ODBC object, e.g. for a later + drvconnect method call. +
new(dsn,[user,passwd]) +
Connect to an ODBC data source. Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
+

Remarks:

+ The run, + prepare, + do, and info methods + (e.g. tables) + can be invoked with a block. In this case the block is executed with + the ODBC::Statement as parameter. The + run and + do methods use the ODBC API + function SQLExecDirect() when the SQL statement has no + parameters. +
+
+
+

ODBC::Statement

+

+ The class to represent a query and its query result. + The object of this class is created as the result of every query. + You may need to invoke the close + or drop methods for the + finished object for better memory performance. +

+

+ When underlying ODBC SQL functions report an error, + an ODBC::Error + exception with a corresponding error message from the ODBC driver + manager and/or driver is raised. +

+

+ ODBC to Ruby type mapping: +

+ + + + + + + + + + + +
ODBCRuby
SQL_SMALLINT, SQL_INTEGER, SQL_TINYINT, SQL_BITT_FIXNUM, T_BIGNUM
SQL_FLOAT, SQL_DOUBLE, SQL_REALT_FLOAT
SQL_DATE, SQL_TYPE_DATEODBC::Date or Date, + see ODBC::Database.use_time +
SQL_TIME, SQL_TYPE_TIMEODBC::Time or Time, + see ODBC::Database.use_time +
SQL_TIMESTAMP, SQL_TYPE_TIMESTAMPODBC::TimeStamp or Time, + see ODBC::Database.use_time +
all othersT_STRING
+

super class:

+

+ ODBC::Database +

+

mixins:

+

+ Enumerable +

+

methods:

+
+
cancel +
Cancel and close the + ODBC::Statement. +
close +
Close the ODBC::Statement. +
drop +
Close and free the ODBC::Statement. +
column(n) +
Returns information of the n-th column of the query result + as ODBC::Column. +
columns(as_ary=false) +
Returns a hash of column information keyed by column names + (Strings) of the query result. If as_ary is true, + an array is returned. In both cases the elements are + ODBC::Columns. If the hash keys + happen to be not unique later columns get their column number + appended, e.g. FOOBAR, FOOBAR#1. +
columns + {|col| block} +
Iterates over the columns of the query result with col + bound to each ODBC::Column. +
ncols +
Returns the number of columns of the query result. +
nrows +
Returns the number of rows of the query result. +
cursorname[=name] +
Returns or sets the cursor name of the statement. +
ignorecase[=bool] +
Same as + ODBC::Database.ignorecase + but affecting this statement only. + Inherited by the current state of the + ODBC::Database at the time the + statement is created. +
fetch +
Returns the next row of the query result as an array. +
fetch_first +
Returns the first row of the query result as an array. +
fetch_scroll(direction, + offset=1) +
Returns the row addressed by direction, e.g. + SQL_FETCH_LAST as an array. offset is + used for SQL_FETCH_RELATIVE and + SQL_FETCH_ABSOLUTE. +
fetch_many(count) +
Returns an array of the next count rows of the query + result, where each row is an array. +
fetch_all +
Same as fetch_many except that all remaining rows + are returned. +
+ fetch_hash(with_table_names=false,use_sym=false) +
Returns the next row of the query result as a hash keyed by + column names. If with_table_names is true, + the keys are combined table/column names. For uniqueness of keys + the same rules as in the columns + method are applied. If use_sym is true, + the keys are formed by intern'ing the (table and) column names. + Alternatively, one hash argument can be presented to + fetch_hash, e.g. + { :key => :Symbol, :table_names => false } or + { :key => :String, :table_names => true } or + { :key => :Fixnum }. +
each {|row| + block} +
Iterates over the query result, performing a fetch + for each row. +
+ each_hash(with_table_names=false,use_sym=false) + {|row| block} +
Iterates over the query result, performing a + fetch_hash for each row. The same + rules for arguments as in fetch_hash apply. +
execute([args...]) +
Binds args to current query and executes it. +
+ run(sql[,args...]) +
Prepares and executes the query specified by sql + with parameters bound from args. +
+ prepare(sql) +
Prepares the query specified by sql. +
more_results +
Returns true and switches over to the next result set, + if the query produced more than one result set. Otherwise + returns false. +
+ get_option(option) +
Gets a statement level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. This method returns the integer value of that option. +
+ set_option(option,intval) +
Sets a statement level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. The second parameter intval can be used to + set driver-specific statement options. +
+ concurrency[=intval] +
Sets or queries the concurrency mode of cursors opened on + the statement. +
maxrows[=intval] +
Sets or queries the maximum rows returned by the statement. +
timeout[=intval] +
Sets or queries the number of seconds to wait for the statement + to execute before returning. +
+ maxlength[=intval] +
Sets or queries the maximum amount of data that will be returned + from a character or binary column. +
+ cursortype[=intval] +
Sets or queries the cursor type used for fetches. +
noscan[=bool] +
Sets or queries whether the driver scans SQL strings for ODBC + escape clauses. +
rowsetsize +
Returns the number of rows in a rowset fetched internally by + a call to SQLExtendedFetch (hardcoded to 1). +
nparams +
Returns the number of parameters of the statement. +
parameter(n) +
Returns information of the n-th parameter of the query + as ODBC::Parameter. +
parameters +
Returns an array of + ODBC::Parameters describing + all parameters of the query. +
param_type(n[,type,coldef,scale]) +
Allows to override the type of parameter n + and returns the current type. This can be useful + when the ODBC driver does not provide proper type information + on SQLDescribeParam. +
param_iotype(n[,iotype]) +
Allows to change the input/output type of parameter n + and returns the current input/output type. By + default, all parameters are ODBC::SQL_PARAM_INPUT. + When calling a stored procedure in the statement, the output + parameter must be altered using this method, e.g. +
# assume 1st parameter is input, 2nd is output
+stmt = conn.prepare("call stored_proc( ?, ?)")
+# setup 2nd for output of an integer
+stmt.param_iotype(2, ODBC::SQL_PARAM_OUTPUT)
+stmt.param_output_type(2, ODBC::SQL_INTEGER)
+stmt.param_output_size(2, 4)
+# execute stmt and retrieve value of output parameter
+stmt.execute(42, nil)
+stmt.fetch_all
+out_value = stmt.param_output_value(2);
+
param_output_type(n[,type]) +
Allows to change the SQL type of output parameter n + and returns the current type. For further + information see the sample code in the + param_iotype method. +
param_output_size(n[,size]) +
Allows to change the byte size of the buffer for output parameter + n and returns the current size. + For further information see the sample code in the + param_iotype method. +
param_output_value(n) +
Returns the value of the output parameter n. + For further information see the sample code in the + param_iotype method. +
make_proc([n]) +
Wraps the statement into a ODBCProc object + and returns that object. The optional n argument is + the parameter number of the statement which is used as output + parameter in the wrapped ODBCProc. For + further information refer to the samples in the description of + ODBC::Database.proc. +
+

singleton methods:

+
+
make_proc(stmt,[n]) +
Wraps the statement stmt into a + ODBCProc object + and returns that object. The optional n argument is + the parameter number of the statement which is used as output + parameter in the wrapped ODBCProc. For + further information refer to the samples in the description of + ODBC::Database.proc. +
+

Remarks:

+ The fetch, + fetch_hash, + and execute + methods can be invoked with a block. In this case the block is + executed with one row of the result set (fetch and + fetch_hash) or with the + ODBC::Statement (execute) + as parameter. +

If the ignorecase option + is turned on, all column names used in the column, + columns, and *_hash methods are converted to + upper case. +

+
+
+

ODBC::Column

+

+ The class to represent (read-only) information of a column of a query. + Objects of this class are created as result of the + column + and columns methods of + ODBC::Statement. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name +
Returns the column name (String). +
table +
Returns the table name (String). +
length +
Returns the length of the column (Integer). +
nullable +
Returns the nullable state of the column (Boolean). +
searchable +
Returns the searchable state of the column (Boolean). +
unsigned +
Returns the unsigned flag of the column (Boolean). +
precision +
Returns the precision of the column (Integer). +
scale +
Returns the scale of the column (Integer). +
type +
Returns the SQL type of the column (Integer). +
autoincrement +
Returns true if the column automatically increments, + false, if not, and nil if that information cannot be + determined from the column. +
+
+
+
+

ODBC::Parameter

+

+ The class to represent (read-only) information of a parameter of a + query. + Objects of this class are created as result of the + parameter and + parameters methods of + ODBC::Statement. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
type +
Returns the parameter's type, e.g. ODBC::SQL_CHAR. +
precision +
Returns the parameter's precision (Integer). +
length +
Returns the parameter's scale (Integer). +
nullable +
Returns the nullable state of the column (Boolean). +
iotype +
Returns the parameter's input/output type, + e.g. ODBC::SQL_PARAM_INPUT. +
output_type +
Returns the parameter's output type, only useful when the + parameter is an output parameter (ODBC::SQL_PARAM_OUTPUT or + ODBC::SQL_PARAM_INPUT_OUTPUT). +
output_size +
Returns the parameter's output buffer size, only useful when the + parameter is an output parameter (ODBC::SQL_PARAM_OUTPUT). +
+
+
+
+

ODBC::Date

+

+ The class to represent a SQL_DATE column in a table + or a SQL_DATE query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(adate) +
Comparison, compares date with adate and returns + 0, 1, or -1. +
day[=num] +
Returns or sets the day component of the date object. +
month[=num] +
Returns or sets the month component of the date object. +
year[=num] +
Returns or sets the year component of the date object. +
to_s +
Returns a string representation of the object with format + YYYY-MM-DD. +
clone +
Returns a fresh copy of the date object. +
+

singleton methods:

+
+
new([year, month, + day]) +
new(date) +
new(time) +
new(string) +
Creates a new date object from numeric values or from + a Date, Time, or String + object. Recognized string formats are e.g. +
2001-01-01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{d '2001-01-01'}
+
+
+
+
+

ODBC::Time

+

+ The class to represent a SQL_TIME column in a table + or a SQL_TIME query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(atime) +
Comparison, compares time with atime and returns + 0, 1, or -1. +
second[=num] +
Returns or sets the second component of the time object. +
minute[=num] +
Returns or sets the minute component of the time object. +
hour[=num] +
Returns or sets the hour component of the time object. +
to_s +
Returns a string representation of the object with format + hh:mm:ss. +
clone +
Returns a fresh copy of the time object. +
+

singleton methods:

+
+
new([hour, minute, + second]) +
new(time) +
new(string) +
Creates a new time object from numeric values or from + a Time or String object. + Recognized string formats are e.g. +
12:00:01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{t '12:00:01'}
+
+
+
+
+

ODBC::TimeStamp

+

+ The class to represent a SQL_TIMESTAMP column in a table + or a SQL_TIMESTAMP query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(atimestamp) +
Comparison, compares time stamp with atimestamp + and returns 0, 1, or -1. +
fraction[=num] +
Returns or sets the fraction component of the time stamp object. + Note that this is expressed in nanoseconds. +
second[=num] +
Returns or sets the second component of the time stamp object. +
minute[=num] +
Returns or sets the minute component of the time stamp object. +
hour[=num] +
Returns or sets the hour component of the time stamp object. +
day[=num] +
Returns or sets the day component of the time stamp object. +
month[=num] +
Returns or sets the month component of the time stamp object. +
year[=num] +
Returns or sets the year component of the time stamp object. +
to_s +
Returns a string representation of the object with format + YYYY-MM-DD hh:mm:ss fraction. +
clone +
Returns a fresh copy of the time stamp object. +
+

singleton methods:

+
+
new([year, month, + day, hour, minute, + second, fraction]) +
new(time) +
new(string) +
Creates a new time stamp object from numeric values or from + a Time or String object. + Recognized string formats are e.g. +
12:00:01
+2001-01-01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{d '2001-01-01'}
+{t '12:00:01'}
+
+
+
+
+

ODBC::DSN

+

+ The class to represent a data source name. Objects of this class are + created as result of a + ODBC::datasources + module function call. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name[=name] +
Queries or sets the name (String) of the data source. +
descr[=descr] +
Queries or sets the descr (description, String) + of the data source. +
+

singleton methods:

+
+
new +
Returns an empty ODBC::DSN object. +
+
+
+
+

ODBC::Driver

+

+ The class to represent an ODBC driver with name and attributes. + Objects of this class are + created as result of a + ODBC::drivers + module function call. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name[=name] +
Queries or sets the name (String) of the ODBC driver. +
attrs[[key][=value]] +
Queries or sets attributes in the attrs Hash of the + ODBC driver object. The keys and values + should be Strings. +
+

singleton methods:

+
+
new +
Returns an empty ODBC::Driver object. +
+
+
+
+

ODBCProc

+

+ The class to represent a procedure with ODBC database/statement + context. + Objects of this class are created as result of a + ODBC::Database.proc method call. +

+

super class:

+

+ Proc +

+

methods:

+
+
call([args*]) +
Executes the SQL statement with parameters set from args + and then invokes the procedure's block, setting the block's + single parameter to the + ODBC::Statement. +
[[args*]] +
Synonym for call. +
+
+
+
+

ODBC::Error

+

+ The class to represent ODBC related exceptions. The descriptive + string is made up of the first ODBC driver or driver manager + message as concatenation of SQL state, native error, driver manager + name, database name/vendor, DSN, and error text, e.g.

+S1000 (1146) [unixODBC][TCX][MySQL]Table 'test.foo' doesn't exist
+ For internally generated errors, e.g. method invocation on + a broken connection, the descriptive string starts with + 'INTERN' and native error 0, e.g.
+INTERN (0) [RubyODBC]No connection
+ For errors programmatically generated by the + raise method, + the descriptive string starts with 'INTERN' and native error 1, + e.g.
+INTERN (1) [RubyODBC]Programmer forgot to RTFM
+

+

super class:

+

+ StandardError +

+
+
+
+

Undocumented

+

Use The Source, Luke!

+

+ ODBC::add_dsn(driver,issys=false)
+ ODBC::add_dsn(name,attrs,issys=false)
+ ODBC::config_dsn(driver,issys=false)
+ ODBC::config_dsn(name,attrs,issys=false)
+ ODBC::del_dsn(driver,issys=false)
+ ODBC::del_dsn(name,attrs,issys=false)
+ ODBC::write_file_dsn(filename,appname,key[,value])
+ ODBC::read_file_dsn(filename,appname,key)
+ ODBC::trace([mask])
+
+ ODBC::Statement.fetch!
+ ODBC::Statement.fetch_first!
+ ODBC::Statement.fetch_first_hash(with_table_names=false,use_sym=false)
+ ODBC::Statement.fetch_scroll!(direction,offset=1)
+ ODBC::Statement.fetch_hash!(with_table_names=false,use_sym=false)
+

+
+
+ mailto:Christian Werner +
+ + diff --git a/ext/extconf.rb b/ext/extconf.rb new file mode 100644 index 0000000..d252510 --- /dev/null +++ b/ext/extconf.rb @@ -0,0 +1,157 @@ +require 'mkmf' + +if ! defined? PLATFORM + PLATFORM = RUBY_PLATFORM +end + +def have_library_ex(lib, func="main", headers=nil) + checking_for "#{func}() in -l#{lib}" do + libs = append_library($libs, lib) + if !func.nil? && !func.empty? && COMMON_LIBS.include?(lib) + true + elsif try_func(func, libs, headers) + $libs = libs + true + else + false + end + end +end + +dir_config("odbc") +have_header("version.h") +have_header("ruby/version.h") +have_header("sql.h") || begin + puts "ERROR: sql.h not found" + exit 1 +end +have_header("sqlext.h") || begin + puts "ERROR: sqlext.h not found" + exit 1 +end +testdlopen = enable_config("dlopen", false) +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLTCHAR", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLTCHAR manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLTCHAR" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLLEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLLEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLLEN" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLULEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLULEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLULEN" +end +$have_odbcinst_h = have_header("odbcinst.h") +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLROWOFFSET", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLROWOFFSET manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLROWOFFSET" +end +$have_odbcinst_h = have_header("odbcinst.h") +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLROWSETSIZE", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLROWSETSIZE manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLROWSETSIZE" +end +$have_odbcinst_h = have_header("odbcinst.h") + +if PLATFORM =~ /mswin32/ then + if !have_library_ex("odbc32", "SQLAllocConnect", "sql.h") || + !have_library_ex("odbccp32", "SQLConfigDataSource", "odbcinst.h") || + !have_library_ex("odbccp32", "SQLInstallerError", "odbcinst.h") || + !have_library("user32", "CharUpper") then + puts "Can not locate odbc libraries" + exit 1 + end + have_func("SQLInstallerError", "odbcinst.h") +# mingw untested !!! +elsif PLATFORM =~ /(mingw|cygwin)/ then + have_library("odbc32") + have_library("odbccp32") + have_library("user32") +elsif (testdlopen && PLATFORM !~ /(macos|darwin)/ && CONFIG["CC"] =~ /gcc/ && have_func("dlopen", "dlfcn.h") && have_library("dl", "dlopen")) then + $LDFLAGS+=" -Wl,-init -Wl,ruby_odbc_init -Wl,-fini -Wl,ruby_odbc_fini" + $CPPFLAGS+=" -DHAVE_SQLCONFIGDATASOURCE" + $CPPFLAGS+=" -DHAVE_SQLINSTALLERERROR" + $CPPFLAGS+=" -DUSE_DLOPEN_FOR_ODBC_LIBS" + if defined? have_type then + if have_type("SQLBIGINT", "sqltypes.h", "-DHAVE_LONG_LONG") then + $CPPFLAGS+=" -DHAVE_LONG_LONG" + end + end +else + have_library("odbc", "SQLAllocConnect") || + have_library("iodbc", "SQLAllocConnect") + ($have_odbcinst_h && + have_library("odbcinst", "SQLConfigDataSource")) || + ($have_odbcinst_h && + have_library("iodbcinst", "SQLConfigDataSource")) + $have_odbcinst_h && + have_func("SQLInstallerError", "odbcinst.h") + if defined? have_type then + if have_type("SQLBIGINT", "sqltypes.h", "-DHAVE_LONG_LONG") then + $CPPFLAGS+=" -DHAVE_LONG_LONG" + end + end +end + +create_makefile("odbc") diff --git a/ext/init.c b/ext/init.c new file mode 100644 index 0000000..fa40639 --- /dev/null +++ b/ext/init.c @@ -0,0 +1,197 @@ +/* + * Part of ODBC-Ruby binding + * Copyright (c) 2006-2007 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: init.c,v 1.6 2007/04/07 09:39:08 chw Exp chw $ + */ + +#include "ruby.h" + +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + +/* + * This module acts as a drop-in replacement for linking with + * "-lodbc -lodbcinst" or "-liodbc -liodbcinst" when dlopen() + * is supported. + * + * Setting the environment variable RUBY_ODBC_DM can be used + * to force loading a specific driver manager shared library. + * Same logic is used with RUBY_ODBC_INST for the ODBC installer + * shared library. + */ + +#include + +/* Create weak alias and function declarations. */ + +#define WEAKFUNC(name) \ + int __attribute__((weak, alias("__"#name))) name (void); \ + static int __attribute__((unused)) __ ## name (void) \ + { return -1; /* == SQL_ERROR */ } + +#define WEAKFUNC_BOOL(name) \ + int __attribute__((weak, alias("__"#name))) name (void); \ + static int __attribute__((unused)) __ ## name (void) \ + { return 0; /* == BOOL/FALSE */ } + +WEAKFUNC(SQLAllocConnect) +WEAKFUNC(SQLAllocEnv) +WEAKFUNC(SQLAllocStmt) +WEAKFUNC(SQLBindParameter) +WEAKFUNC(SQLCancel) +WEAKFUNC(SQLDescribeParam) +WEAKFUNC(SQLDisconnect) +WEAKFUNC(SQLExecute) +WEAKFUNC(SQLFetch) +WEAKFUNC(SQLFetchScroll) +WEAKFUNC(SQLFreeConnect) +WEAKFUNC(SQLFreeEnv) +WEAKFUNC(SQLFreeStmt) +WEAKFUNC(SQLGetData) +WEAKFUNC(SQLGetEnvAttr) +WEAKFUNC(SQLGetStmtOption) +WEAKFUNC(SQLMoreResults) +WEAKFUNC(SQLNumParams) +WEAKFUNC(SQLNumResultCols) +WEAKFUNC(SQLRowCount) +WEAKFUNC(SQLSetEnvAttr) +WEAKFUNC(SQLSetStmtOption) +WEAKFUNC(SQLTransact) +WEAKFUNC(SQLEndTran) + +WEAKFUNC(SQLColAttributes) +WEAKFUNC(SQLColAttributesW) +WEAKFUNC(SQLColumns) +WEAKFUNC(SQLColumnsW) +WEAKFUNC(SQLConnect) +WEAKFUNC(SQLConnectW) +WEAKFUNC(SQLDataSources) +WEAKFUNC(SQLDataSourcesW) +WEAKFUNC(SQLDriverConnect) +WEAKFUNC(SQLDriverConnectW) +WEAKFUNC(SQLDrivers) +WEAKFUNC(SQLDriversW) +WEAKFUNC(SQLError) +WEAKFUNC(SQLErrorW) +WEAKFUNC(SQLExecDirect) +WEAKFUNC(SQLExecDirectW) +WEAKFUNC(SQLForeignKeys) +WEAKFUNC(SQLForeignKeysW) +WEAKFUNC(SQLGetConnectOption) +WEAKFUNC(SQLGetConnectOptionW) +WEAKFUNC(SQLGetCursorName) +WEAKFUNC(SQLGetCursorNameW) +WEAKFUNC(SQLGetInfo) +WEAKFUNC(SQLGetInfoW) +WEAKFUNC(SQLGetTypeInfo) +WEAKFUNC(SQLGetTypeInfoW) +WEAKFUNC(SQLPrepare) +WEAKFUNC(SQLPrepareW) +WEAKFUNC(SQLPrimaryKeys) +WEAKFUNC(SQLPrimaryKeysW) +WEAKFUNC(SQLProcedureColumns) +WEAKFUNC(SQLProcedureColumnsW) +WEAKFUNC(SQLProcedures) +WEAKFUNC(SQLProceduresW) +WEAKFUNC(SQLSetConnectOption) +WEAKFUNC(SQLSetConnectOptionW) +WEAKFUNC(SQLSetCursorName) +WEAKFUNC(SQLSetCursorNameW) +WEAKFUNC(SQLSpecialColumns) +WEAKFUNC(SQLSpecialColumnsW) +WEAKFUNC(SQLStatistics) +WEAKFUNC(SQLStatisticsW) +WEAKFUNC(SQLTablePrivileges) +WEAKFUNC(SQLTablePrivilegesW) +WEAKFUNC(SQLTables) +WEAKFUNC(SQLTablesW) +WEAKFUNC(SQLInstallerError) +WEAKFUNC(SQLInstallerErrorW) + +WEAKFUNC_BOOL(SQLConfigDataSource) +WEAKFUNC_BOOL(SQLConfigDataSourceW) +WEAKFUNC_BOOL(SQLReadFileDSN) +WEAKFUNC_BOOL(SQLReadFileDSNW) +WEAKFUNC_BOOL(SQLWriteFileDSN) +WEAKFUNC_BOOL(SQLWriteFileDSNW) + +/* Library initializer and finalizer. */ + +static void *lib_odbc = 0; +static void *lib_odbcinst = 0; + +#define warn(msg) fputs(msg, stderr) + +void +ruby_odbc_init() +{ + int useiodbc = 0; + char *dm_name = getenv("RUBY_ODBC_DM"); + char *inst_name = getenv("RUBY_ODBC_INST"); + + if (dm_name) { + lib_odbc = dlopen(dm_name, RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + warn("WARNING: $RUBY_ODBC_DM not loaded.\n"); + } else { + if (inst_name) { + lib_odbcinst = dlopen(inst_name, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbcinst) { + warn("WARNING: $RUBY_ODBC_INST not loaded.\n"); + } + return; + } + } + lib_odbc = dlopen("libodbc" DLEXT ".1", RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + lib_odbc = dlopen("libodbc" DLEXT, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbc) { + lib_odbc = dlopen("libiodbc" DLEXT ".2", RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + lib_odbc = dlopen("libiodbc" DLEXT, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbc) { + warn("WARNING: no ODBC driver manager found.\n"); + return; + } + useiodbc = 1; + } + lib_odbcinst = dlopen(useiodbc ? + "libiodbcinst" DLEXT ".2" : "libodbcinst" DLEXT ".1", + RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbcinst) { + lib_odbcinst = dlopen(useiodbc ? + "libiodbcinst" DLEXT : "libodbcinst" DLEXT, + RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbcinst) { + warn("WARNING: no ODBC installer library found.\n"); + } +} + +void +ruby_odbc_fini() +{ + if (lib_odbcinst) { + dlclose(lib_odbcinst); + lib_odbcinst = 0; + } + if (lib_odbc) { + dlclose(lib_odbc); + lib_odbc = 0; + } +} + +int +ruby_odbc_have_func(char *name, void *addr) +{ + return name && addr && (dlsym(NULL, name) != addr); +} + +#endif diff --git a/ext/odbc.c b/ext/odbc.c new file mode 100644 index 0000000..098836f --- /dev/null +++ b/ext/odbc.c @@ -0,0 +1,9475 @@ +/* + * ODBC-Ruby binding + * Copyright (c) 2001-2020 Christian Werner + * Portions copyright (c) 2004 Ryszard Niewisiewicz + * Portions copyright (c) 2006 Carl Blakeley + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: odbc.c,v 1.80 2020/12/25 21:45:42 chw Exp chw $ + */ + +#undef ODBCVER + +#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) +#include +#endif +#include +#include +#include "ruby.h" +#ifdef HAVE_VERSION_H +#include "version.h" +#endif +#ifdef HAVE_RUBY_VERSION_H +#include "ruby/version.h" +#endif +#ifdef HAVE_SQL_H +#include +#else +#error Missing include: sql.h +#endif +#ifdef HAVE_SQLEXT_H +#include +#else +#error Missing include: sqlext.h +#endif +#ifdef HAVE_ODBCINST_H +#include +#endif + +#ifdef UNICODE +#include +#endif + +#ifndef HAVE_TYPE_SQLTCHAR +#ifdef UNICODE +typedef SQLWCHAR SQLTCHAR; +#else +typedef SQLCHAR SQLTCHAR; +#endif +#endif + +#ifndef HAVE_TYPE_SQLLEN +#define SQLLEN SQLINTEGER +#endif +#ifndef HAVE_TYPE_SQLULEN +#define SQLULEN SQLUINTEGER +#endif +#ifndef HAVE_TYPE_SQLROWOFFSET +#define SQLROWOFFSET SQLLEN +#endif +#ifndef HAVE_TYPE_SQLROWSETSIZE +#define SQLROWSETSIZE SQLULEN +#endif + +#if (RUBY_VERSION_MAJOR <= 1) && (RUBY_VERSION_MINOR < 9) +#define TIME_USE_USEC 1 +#endif + +#if (RUBY_API_VERSION_CODE >= 20500) +#define FUNCALL_NOARGS(o, m) rb_funcall((o), (m), 0) +#else +#define FUNCALL_NOARGS(o, m) rb_funcall((o), (m), 0, NULL) +#endif + +#ifdef HAVE_RUBY_THREAD_H +#include "ruby/thread.h" +#endif + +/* + * Conditionally undefine aliases of ODBC installer UNICODE functions. + */ + +#if defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW) +#undef SQLInstallerError +#endif +#if defined(UNICODE) && defined(HAVE_SQLCONFIGDATASOURCEW) +#undef SQLConfigDataSource +#endif +#if defined(UNICODE) && defined(HAVE_SQLREADFILEDSNW) +#undef SQLReadFileDSN +#endif +#if defined(UNICODE) && defined(HAVE_SQLWRITEFILEDSNW) +#undef SQLWriteFileDSN +#endif + +#if defined(UNICODE) && defined(USE_DLOPEN_FOR_ODBC_LIBS) +extern int ruby_odbc_have_func(const char *name, void *addr); +#endif + +#ifdef UNICODE +/* + * Declarations of required installer APIs in case + * header files don't provide them (unixODBC?). + */ + +#ifndef HAVE_SQLINSTALLERERRORW +SQLRETURN INSTAPI SQLInstallerErrorW(WORD, DWORD *, LPWSTR, WORD, WORD *); +#endif +#ifndef HAVE_SQLCONFIGDATASOURCEW +BOOL INSTAPI SQLConfigDataSourceW(HWND, WORD, LPWSTR, LPWSTR); +#endif +#ifndef HAVE_SQLREADFILEDSNW +BOOL INSTAPI SQLReadFileDSNW(LPWSTR, LPWSTR, LPWSTR, LPWSTR, WORD, WORD *); +#endif +#ifndef HAVE_SQLWRITEFILEDSNW +BOOL INSTAPI SQLWriteFileDSNW(LPWSTR, LPWSTR, LPWSTR, LPWSTR); +#endif + +#if defined(HAVE_RUBY_ENCODING_H) && HAVE_RUBY_ENCODING_H +#define USE_RB_ENC 1 +#include "ruby/encoding.h" +static rb_encoding *rb_enc = NULL; +static VALUE rb_encv = Qnil; +#endif + +#endif /* UNICODE */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +#define rb_define_alloc_func(cls, func) \ + rb_define_singleton_method(cls, "new", func, -1) +#define rb_undefine_alloc_func(cls) \ + rb_undef_method(CLASS_OF(cls), "new") +#endif + +#ifdef RB_CVAR_SET_4ARGS +#define CVAR_SET(x, y, z) rb_cvar_set(x, y, z, 0) +#else +#define CVAR_SET(x, y, z) rb_cvar_set(x, y, z) +#endif + +#ifndef STR2CSTR +#define STR2CSTR(x) StringValueCStr(x) +#define NO_RB_STR2CSTR 1 +#endif + +#ifdef TRACING +static int tracing = 0; +#define tracemsg(t, x) {if (tracing & t) { x }} +static SQLRETURN tracesql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, + SQLRETURN ret, const char *m); +#else +#define tracemsg(t, x) +#define tracesql(a, b, c, d, e) d +#endif + +#ifndef SQL_SUCCEEDED +#define SQL_SUCCEEDED(x) \ + (((x) == SQL_SUCCESS) || ((x) == SQL_SUCCESS_WITH_INFO)) +#endif + +#ifndef SQL_NO_DATA +#define SQL_NO_DATA SQL_NO_DATA_FOUND +#endif + +typedef struct link { + struct link *succ; + struct link *pred; + struct link *head; + int offs; +} LINK; + +typedef struct env { + VALUE self; + LINK dbcs; + SQLHENV henv; +} ENV; + +typedef struct dbc { + LINK link; + VALUE self; + VALUE env; + struct env *envp; + LINK stmts; + SQLHDBC hdbc; + VALUE rbtime; + VALUE gmtime; + int upc; + VALUE use_sql_column_name; +} DBC; + +typedef struct { + SQLSMALLINT type; + SQLULEN coldef; + SQLULEN coldef_max; + SQLSMALLINT scale; + SQLLEN rlen; + SQLSMALLINT nullable; + SQLSMALLINT iotype; + int override; +#ifdef UNICODE + SQLWCHAR *tofree; +#endif + char buffer[sizeof (double) * 4 + sizeof (TIMESTAMP_STRUCT)]; + SQLSMALLINT ctype; + SQLSMALLINT outtype; + int outsize; + char *outbuf; +} PARAMINFO; + +typedef struct { + int type; + int size; +} COLTYPE; + +typedef struct stmt { + LINK link; + VALUE self; + VALUE dbc; + struct dbc *dbcp; + SQLHSTMT hstmt; + int nump; + PARAMINFO *paraminfo; + int ncols; + COLTYPE *coltypes; + char **colnames; + VALUE *colvals; + char **dbufs; + int fetchc; + int upc; + int usef; +} STMT; + +static VALUE Modbc; +static VALUE Cobj; +static VALUE Cenv; +static VALUE Cdbc; +static VALUE Cstmt; +static VALUE Ccolumn; +static VALUE Cparam; +static VALUE Cerror; +static VALUE Cdsn; +static VALUE Cdrv; +static VALUE Cdate; +static VALUE Ctime; +static VALUE Ctimestamp; +static VALUE Cproc; +static VALUE rb_cDate; + +static ID IDstart; +static ID IDatatinfo; +static ID IDataterror; +static ID IDkeys; +static ID IDatattrs; +static ID IDday; +static ID IDmonth; +static ID IDyear; +static ID IDmday; +static ID IDnsec; +static ID IDusec; +static ID IDsec; +static ID IDmin; +static ID IDhour; +static ID IDusec; +static ID IDkeyp; +static ID IDkey; +static ID IDSymbol; +static ID IDString; +static ID IDFixnum; +static ID IDtable_names; +static ID IDnew; +static ID IDnow; +static ID IDname; +static ID IDtable; +static ID IDtype; +static ID IDlength; +static ID IDnullable; +static ID IDscale; +static ID IDprecision; +static ID IDsearchable; +static ID IDunsigned; +static ID IDiotype; +static ID IDoutput_size; +static ID IDoutput_type; +static ID IDdescr; +static ID IDstatement; +static ID IDreturn_output_param; +static ID IDattrs; +static ID IDNULL; +static ID IDdefault; +#ifdef USE_RB_ENC +static ID IDencode; +#endif +static ID IDparse; +static ID IDutc; +static ID IDlocal; +static ID IDto_s; + +/* + * Modes for dbc_info + */ + +#define INFO_TABLES 0 +#define INFO_COLUMNS 1 +#define INFO_PRIMKEYS 2 +#define INFO_INDEXES 3 +#define INFO_TYPES 4 +#define INFO_FORKEYS 5 +#define INFO_TPRIV 6 +#define INFO_PROCS 7 +#define INFO_PROCCOLS 8 +#define INFO_SPECCOLS 9 + +/* + * Modes for make_result/stmt_exec_int + */ + +#define MAKERES_BLOCK 1 +#define MAKERES_NOCLOSE 2 +#define MAKERES_PREPARE 4 +#define MAKERES_EXECD 8 +#define EXEC_PARMXNULL(x) (16 | ((x) << 5)) +#define EXEC_PARMXOUT(x) (((x) & 16) ? ((x) >> 5) : -1) + +/* + * Modes for do_fetch + */ + +#define DOFETCH_ARY 0 +#define DOFETCH_HASH 1 +#define DOFETCH_HASH2 2 +#define DOFETCH_HASHK 3 +#define DOFETCH_HASHK2 4 +#define DOFETCH_HASHN 5 +#define DOFETCH_MODES 7 +#define DOFETCH_BANG 8 + +/* + * Size of segment when SQL_NO_TOTAL + */ + +#define SEGSIZE 65536 + +/* + * Forward declarations. + */ + +static SQLRETURN callsql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, + SQLRETURN ret, const char *m); + +static VALUE stmt_exec(int argc, VALUE *argv, VALUE self); +static VALUE stmt_each(VALUE self); +static VALUE stmt_each_hash(int argc, VALUE *argv, VALUE self); +static VALUE stmt_close(VALUE self); +static VALUE stmt_drop(VALUE self); + +/* + * Column name buffers on statement. + */ + +static const char *colnamebuf[] = { + "@_c0", "@_c1", "@_c2", "@_c3" +}; + +/* + * Macro to align buffers. + */ + +#define LEN_ALIGN(x) \ + ((x) + sizeof (double) - (((x) + sizeof (double)) % sizeof (double))) + + +/* + *---------------------------------------------------------------------- + * + * Wrappers for long running SQL APIs with GVL released. + * + *---------------------------------------------------------------------- + */ + +#ifdef RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_ + +static void +empty_ubf(void *args) +{ +} + +struct S_SQLCONNECT { + SQLHDBC hdbc; + SQLTCHAR *dsn; + SQLSMALLINT dsn_len; + SQLTCHAR *usr; + SQLSMALLINT usr_len; + SQLTCHAR *pwd; + SQLSMALLINT pwd_len; +}; + +static void * +F_SQLCONNECT(void *args) +{ + size_t ret; + struct S_SQLCONNECT *argp = (struct S_SQLCONNECT *) args; + + ret = SQLConnect(argp->hdbc, argp->dsn, argp->dsn_len, + argp->usr, argp->usr_len, argp->pwd, argp->pwd_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLCONNECT(SQLHDBC hdbc, SQLTCHAR *dsn, SQLSMALLINT dsn_len, SQLTCHAR *usr, + SQLSMALLINT usr_len, SQLTCHAR *pwd, SQLSMALLINT pwd_len) +{ + size_t ret; + struct S_SQLCONNECT arg; + + arg.hdbc = hdbc; + arg.dsn = dsn; + arg.dsn_len = dsn_len; + arg.usr = usr; + arg.usr_len = usr_len; + arg.pwd = pwd; + arg.pwd_len = pwd_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLDRIVERCONNECT { + SQLHDBC hdbc; + SQLHWND hwnd; + SQLTCHAR *connin; + SQLSMALLINT connin_len; + SQLTCHAR *connout; + SQLSMALLINT connout_max; + SQLSMALLINT *connout_len; + SQLUSMALLINT compl; +}; + +static void * +F_SQLDRIVERCONNECT(void *args) +{ + size_t ret; + struct S_SQLDRIVERCONNECT *argp = (struct S_SQLDRIVERCONNECT *) args; + + ret = SQLDriverConnect(argp->hdbc, argp->hwnd, argp->connin, + argp->connin_len, argp->connout, argp->connout_max, + argp->connout_len, argp->compl); + return (void *) ret; +} + +static inline SQLRETURN +SQLDRIVERCONNECT(SQLHDBC hdbc, SQLHWND hwnd, SQLTCHAR *connin, + SQLSMALLINT connin_len, SQLTCHAR *connout, + SQLSMALLINT connout_max, SQLSMALLINT *connout_len, + SQLUSMALLINT compl) +{ + size_t ret; + struct S_SQLDRIVERCONNECT arg; + + arg.hdbc = hdbc; + arg.hwnd = hwnd; + arg.connin = connin; + arg.connin_len = connin_len; + arg.connout = connout; + arg.connout_max = connout_max; + arg.connout_len = connout_len; + arg.compl = compl; + ret = (size_t) rb_thread_call_without_gvl(F_SQLDRIVERCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLDISCONNECT { + SQLHSTMT hstmt; +}; + +static void * +F_SQLDISCONNECT(void *args) +{ + size_t ret; + struct S_SQLDISCONNECT *argp = (struct S_SQLDISCONNECT *) args; + + ret = SQLDisconnect(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLDISCONNECT(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLDISCONNECT arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLDISCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLTABLES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLTCHAR *typ; + SQLSMALLINT typ_len; +}; + +static void * +F_SQLTABLES(void *args) +{ + size_t ret; + struct S_SQLTABLES *argp = (struct S_SQLTABLES *) args; + + ret = SQLTables(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->typ, argp->typ_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLTABLES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLTCHAR *typ, SQLSMALLINT typ_len) +{ + size_t ret; + struct S_SQLTABLES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.typ = typ; + arg.typ_len = typ_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTABLES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLCOLUMNS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLTCHAR *col; + SQLSMALLINT col_len; +}; + +static void * +F_SQLCOLUMNS(void *args) +{ + size_t ret; + struct S_SQLCOLUMNS *argp = (struct S_SQLCOLUMNS *) args; + + ret = SQLColumns(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->col, argp->col_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLCOLUMNS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLTCHAR *col, SQLSMALLINT col_len) +{ + size_t ret; + struct S_SQLCOLUMNS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.col = col; + arg.col_len = col_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLCOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPRIMARYKEYS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; +}; + +static void * +F_SQLPRIMARYKEYS(void *args) +{ + size_t ret; + struct S_SQLPRIMARYKEYS *argp = (struct S_SQLPRIMARYKEYS *) args; + + ret = SQLPrimaryKeys(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, argp->tbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPRIMARYKEYS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len) +{ + size_t ret; + struct S_SQLPRIMARYKEYS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPRIMARYKEYS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLFOREIGNKEYS { + SQLHSTMT hdbc; + SQLTCHAR *pkcat; + SQLSMALLINT pkcat_len; + SQLTCHAR *pksch; + SQLSMALLINT pksch_len; + SQLTCHAR *pktbl; + SQLSMALLINT pktbl_len; + SQLTCHAR *fkcat; + SQLSMALLINT fkcat_len; + SQLTCHAR *fksch; + SQLSMALLINT fksch_len; + SQLTCHAR *fktbl; + SQLSMALLINT fktbl_len; +}; + +static void * +F_SQLFOREIGNKEYS(void *args) +{ + size_t ret; + struct S_SQLFOREIGNKEYS *argp = (struct S_SQLFOREIGNKEYS *) args; + + ret = SQLForeignKeys(argp->hdbc, argp->pkcat, argp->pkcat_len, + argp->pksch, argp->pksch_len, + argp->pktbl, argp->pktbl_len, + argp->fkcat, argp->fkcat_len, + argp->fksch, argp->fksch_len, + argp->fktbl, argp->fktbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLFOREIGNKEYS(SQLHDBC hdbc, SQLTCHAR *pkcat, SQLSMALLINT pkcat_len, + SQLTCHAR *pksch, SQLSMALLINT pksch_len, SQLTCHAR *pktbl, + SQLSMALLINT pktbl_len, SQLTCHAR *fkcat, SQLSMALLINT fkcat_len, + SQLTCHAR *fksch, SQLSMALLINT fksch_len, SQLTCHAR *fktbl, + SQLSMALLINT fktbl_len) +{ + size_t ret; + struct S_SQLFOREIGNKEYS arg; + + arg.hdbc = hdbc; + arg.pkcat = pkcat; + arg.pkcat_len = pkcat_len; + arg.pksch = pksch; + arg.pksch_len = pksch_len; + arg.pktbl = pktbl; + arg.pktbl_len = pktbl_len; + arg.fkcat = fkcat; + arg.fkcat_len = fkcat_len; + arg.fksch = fksch; + arg.fksch_len = fksch_len; + arg.fktbl = fktbl; + arg.fktbl_len = fktbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFOREIGNKEYS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPROCEDURES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *prc; + SQLSMALLINT prc_len; +}; + +static void * +F_SQLPROCEDURES(void *args) +{ + size_t ret; + struct S_SQLPROCEDURES *argp = (struct S_SQLPROCEDURES *) args; + + ret = SQLProcedures(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->prc, argp->prc_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPROCEDURES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *prc, + SQLSMALLINT prc_len) +{ + size_t ret; + struct S_SQLPROCEDURES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.prc = prc; + arg.prc_len = prc_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPROCEDURES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPROCEDURECOLUMNS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *prc; + SQLSMALLINT prc_len; + SQLTCHAR *col; + SQLSMALLINT col_len; +}; + +static void * +F_SQLPROCEDURECOLUMNS(void *args) +{ + size_t ret; + struct S_SQLPROCEDURECOLUMNS *argp = (struct S_SQLPROCEDURECOLUMNS *) args; + + ret = SQLProcedureColumns(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->prc, + argp->prc_len, argp->col, argp->col_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPROCEDURECOLUMNS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *prc, + SQLSMALLINT prc_len, SQLTCHAR *col, SQLSMALLINT col_len) +{ + size_t ret; + struct S_SQLPROCEDURECOLUMNS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.prc = prc; + arg.prc_len = prc_len; + arg.col = col; + arg.col_len = col_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPROCEDURECOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLTABLEPRIVILEGES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; +}; + +static void * +F_SQLTABLEPRIVILEGES(void *args) +{ + size_t ret; + struct S_SQLTABLEPRIVILEGES *argp = (struct S_SQLTABLEPRIVILEGES *) args; + + ret = SQLTablePrivileges(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLTABLEPRIVILEGES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len) +{ + size_t ret; + struct S_SQLTABLEPRIVILEGES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTABLEPRIVILEGES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLSTATISTICS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLUSMALLINT uniq; + SQLUSMALLINT resv; +}; + +static void * +F_SQLSTATISTICS(void *args) +{ + size_t ret; + struct S_SQLSTATISTICS *argp = (struct S_SQLSTATISTICS *) args; + + ret = SQLStatistics(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->uniq, argp->resv); + return (void *) ret; +} + +static inline SQLRETURN +SQLSTATISTICS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLUSMALLINT uniq, SQLUSMALLINT resv) +{ + size_t ret; + struct S_SQLSTATISTICS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.uniq = uniq; + arg.resv = resv; + ret = (size_t) rb_thread_call_without_gvl(F_SQLSTATISTICS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLSPECIALCOLUMNS { + SQLHSTMT hdbc; + SQLUSMALLINT idtyp; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLUSMALLINT scope; + SQLUSMALLINT nulbl; +}; + +static void * +F_SQLSPECIALCOLUMNS(void *args) +{ + size_t ret; + struct S_SQLSPECIALCOLUMNS *argp = (struct S_SQLSPECIALCOLUMNS *) args; + + ret = SQLSpecialColumns(argp->hdbc, argp->idtyp, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->scope, argp->nulbl); + return (void *) ret; +} + +static inline SQLRETURN +SQLSPECIALCOLUMNS(SQLHDBC hdbc, SQLUSMALLINT idtyp, SQLTCHAR *cat, + SQLSMALLINT cat_len, SQLTCHAR *sch, SQLSMALLINT sch_len, + SQLTCHAR *tbl, SQLSMALLINT tbl_len, + SQLUSMALLINT scope, SQLUSMALLINT nulbl) +{ + size_t ret; + struct S_SQLSPECIALCOLUMNS arg; + + arg.hdbc = hdbc; + arg.idtyp = idtyp; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.scope = scope; + arg.nulbl = nulbl; + ret = (size_t) rb_thread_call_without_gvl(F_SQLSPECIALCOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLGETTYPEINFO { + SQLHSTMT hdbc; + SQLSMALLINT type; +}; + +static void * +F_SQLGETTYPEINFO(void *args) +{ + size_t ret; + struct S_SQLGETTYPEINFO *argp = (struct S_SQLGETTYPEINFO *) args; + + ret = SQLGetTypeInfo(argp->hdbc, argp->type); + return (void *) ret; +} + +static inline SQLRETURN +SQLGETTYPEINFO(SQLHDBC hdbc, SQLSMALLINT type) +{ + size_t ret; + struct S_SQLGETTYPEINFO arg; + + arg.hdbc = hdbc; + arg.type = type; + ret = (size_t) rb_thread_call_without_gvl(F_SQLGETTYPEINFO, &arg, + empty_ubf, &arg); + return ret; +} + +#if (ODBCVER >= 0x0300) + +struct S_SQLENDTRAN { + SQLSMALLINT htype; + SQLHANDLE handle; + SQLSMALLINT op; +}; + +static void * +F_SQLENDTRAN(void *args) +{ + size_t ret; + struct S_SQLENDTRAN *argp = (struct S_SQLENDTRAN *) args; + + ret = SQLEndTran(argp->htype, argp->handle, argp->op); + return (void *) ret; +} + +static inline SQLRETURN +SQLENDTRAN(SQLUSMALLINT htype, SQLHANDLE handle, SQLUSMALLINT op) +{ + size_t ret; + struct S_SQLENDTRAN arg; + + arg.htype = htype; + arg.handle = handle; + arg.op = op; + ret = (size_t) rb_thread_call_without_gvl(F_SQLENDTRAN, &arg, + empty_ubf, &arg); + return ret; +} + +#else + +struct S_SQLTRANSACT { + SQLHENV henv; + SQLHDBC hdbc; + SQLSMALLINT op; +}; + +static void * +F_SQLTRANSACT(void *args) +{ + size_t ret; + struct S_SQLTRANSACT *argp = (struct S_SQLTRANSACT *) args; + + ret = SQLTransact(argp->henv, argp->hdbc, argp->op); + return (void *) ret; +} + +static inline SQLRETURN +SQLTRANSACT(SQLHENV henv, SQLHDBC hdbc, SQLUSMALLINT op) +{ + size_t ret; + struct S_SQLTRANSACT arg; + + arg.henv = henv; + arg.hdbc = hdbc; + arg.op = op; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTRANCACT, &arg, + empty_ubf, &arg); + return ret; +} + +#endif + +struct S_SQLEXECDIRECT { + SQLHSTMT hstmt; + SQLTCHAR *sql; + SQLINTEGER len; +}; + +static void * +F_SQLEXECDIRECT(void *args) +{ + size_t ret; + struct S_SQLEXECDIRECT *argp = (struct S_SQLEXECDIRECT *) args; + + ret = SQLExecDirect(argp->hstmt, argp->sql, argp->len); + return (void *) ret; +} + +static void +F_SQLEXECDIRECT_UBF(void *args) +{ + struct S_SQLEXECDIRECT *argp = (struct S_SQLEXECDIRECT *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLEXECDIRECT(SQLHSTMT hstmt, SQLTCHAR *sql, SQLINTEGER len) +{ + size_t ret; + struct S_SQLEXECDIRECT arg; + + arg.hstmt = hstmt; + arg.sql = sql; + arg.len = len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXECDIRECT, &arg, + F_SQLEXECDIRECT_UBF, &arg); + return ret; +} + +struct S_SQLEXECUTE { + SQLHSTMT hstmt; +}; + +static void * +F_SQLEXECUTE(void *args) +{ + size_t ret; + struct S_SQLEXECUTE *argp = (struct S_SQLEXECUTE *) args; + + ret = SQLExecute(argp->hstmt); + return (void *) ret; +} + +static void +F_SQLEXECUTE_UBF(void *args) +{ + struct S_SQLEXECUTE *argp = (struct S_SQLEXECUTE *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLEXECUTE(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLEXECUTE arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXECUTE, &arg, + F_SQLEXECUTE_UBF, &arg); + return ret; +} + +struct S_SQLPREPARE { + SQLHSTMT hstmt; + SQLTCHAR *sql; + SQLINTEGER len; +}; + +static void * +F_SQLPREPARE(void *args) +{ + size_t ret; + struct S_SQLPREPARE *argp = (struct S_SQLPREPARE *) args; + + ret = SQLPrepare(argp->hstmt, argp->sql, argp->len); + return (void *) ret; +} + +static void +F_SQLPREPARE_UBF(void *args) +{ + struct S_SQLPREPARE *argp = (struct S_SQLPREPARE *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLPREPARE(SQLHSTMT hstmt, SQLTCHAR *sql, SQLINTEGER len) +{ + size_t ret; + struct S_SQLPREPARE arg; + + arg.hstmt = hstmt; + arg.sql = sql; + arg.len = len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPREPARE, &arg, + F_SQLPREPARE_UBF, &arg); + return ret; +} + +struct S_SQLMORERESULTS { + SQLHSTMT hstmt; +}; + +static void * +F_SQLMORERESULTS(void *args) +{ + size_t ret; + struct S_SQLMORERESULTS *argp = (struct S_SQLMORERESULTS *) args; + + ret = SQLMoreResults(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLMORERESULTS(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLMORERESULTS arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLMORERESULTS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLFETCH { + SQLHSTMT hstmt; +}; + +static void * +F_SQLFETCH(void *args) +{ + size_t ret; + struct S_SQLFETCH *argp = (struct S_SQLFETCH *) args; + + ret = SQLFetch(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLFETCH(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLFETCH arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFETCH, &arg, + empty_ubf, &arg); + return ret; +} + +#if (ODBCVER >= 0x0300) + +struct S_SQLFETCHSCROLL { + SQLHSTMT hstmt; + SQLSMALLINT dir; + SQLROWOFFSET offs; +}; + +static void * +F_SQLFETCHSCROLL(void *args) +{ + size_t ret; + struct S_SQLFETCHSCROLL *argp = (struct S_SQLFETCHSCROLL *) args; + + ret = SQLFetchScroll(argp->hstmt, argp->dir, argp->offs); + return (void *) ret; +} + +static inline SQLRETURN +SQLFETCHSCROLL(SQLHSTMT hstmt, SQLSMALLINT dir, SQLROWOFFSET offs) +{ + size_t ret; + struct S_SQLFETCHSCROLL arg; + + arg.hstmt = hstmt; + arg.dir = dir; + arg.offs = offs; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFETCHSCROLL, &arg, + empty_ubf, &arg); + return ret; +} + +#else + +struct S_SQLEXTENDEDFETCH { + SQLHSTMT hstmt; + SQLUSMALLINT type; + SQLROWOFFSET row; + SQLROWSETSIZE *rowp; + SQLUSMALLINT *status +}; + +static void * +F_SQLEXTENDEDFETCH(void *args) +{ + size_t ret; + struct S_SQLEXTENDEDFETCH *argp = (struct S_SQLEXTENDEDFETCH *) args; + + ret = SQLExtendedFetch(argp->hstmt, argp->type, argp->row, argp->rowp, + argp->status); + return (void *) ret; +} + +static inline SQLRETURN +SQLEXTENDEDFETCH(SQLHSTMT hstmt, SQLUSMALLINT type, SQLROWOFFSET row, + SQLROWSETSIZE *rowp, SQLUSMALLINT *status) +{ + size_t ret; + struct S_SQLEXTENDEDFETCH arg; + + arg.hstmt = hstmt; + arg.type = type; + arg.row = row; + arg.rowp = rowp; + arg.status = status; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXTENDEDFETCH, &arg, + empty_ubf, &arg); + return ret; +} + +#endif + +#else + +#define SQLCONNECT SQLConnect +#define SQLDRIVERCONNECT SQLDriverConnect +#define SQLDISCONNECT SQLDisconnect +#define SQLTABLES SQLTables +#define SQLCOLUMNS SQLColumns +#define SQLPRIMARYKEYS SQLPrimaryKeys +#define SQLFOREIGNKEYS SQLForeignKeys +#define SQLPROCEDURES SQLProcedures +#define SQLPROCEDURECOLUMNS SQLProcedureColumns +#define SQLTABLEPRIVILEGES SQLTablePrivileges +#define SQLSTATISTICS SQLStatistics +#define SQLSPECIALCOLUMNS SQLSpecialColumns +#define SQLGETTYPEINFO SQLGetTypeInfo +#if (ODBCVER >= 0x0300) +#define SQLENDTRAN SQLEndTran +#else +#define SQLTRANSACT SQLTransact +#endif +#define SQLEXECDIRECT SQLExecDirect +#define SQLEXECUTE SQLExecute +#define SQLPREPARE SQLPrepare +#define SQLMORERESULTS SQLMoreResults +#define SQLFETCH SQLFetch +#if (ODBCVER >= 0x0300) +#define SQLFETCHSCROLL SQLFetchScroll +#else +#define SQLEXTENDEDFETCH SQLExtendedFetch +#endif + +#endif + + +/* + *---------------------------------------------------------------------- + * + * UNICODE converters et.al. + * + *---------------------------------------------------------------------- + */ + +#ifdef UNICODE + +static int +uc_strlen(SQLWCHAR *str) +{ + int len = 0; + + if (str != NULL) { + while (*str != '\0') { + ++len; + ++str; + } + } + return len; +} + +static SQLWCHAR * +uc_strchr(SQLWCHAR *str, SQLWCHAR c) +{ + if (str != NULL) { + while ((*str != '\0') && (*str != c)) { + ++str; + } + str = (*str == c) ? str : NULL; + } + return str; +} + +static int +mkutf(char *dest, SQLWCHAR *src, int len) +{ + int i; + char *cp = dest; + + for (i = 0; i < len; i++) { + unsigned long c = src[i]; + + if (sizeof (SQLWCHAR) == (2 * sizeof (char))) { + c &= 0xffff; + } + if (c < 0x80) { + *cp++ = c; + } else if (c < 0x800) { + *cp++ = 0xc0 | ((c >> 6) & 0x1f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (c >= 0xd800) && (c <= 0xdbff) && ((i + 1) < len)) { + unsigned long c2 = src[i + 1] & 0xffff; + + if ((c2 >= 0xdc00) && (c <= 0xdfff)) { + c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000; + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + ++i; + continue; + } + } + *cp++ = 0xe0 | ((c >> 12) & 0x0f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x200000) { + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x4000000) { + *cp++ = 0xf8 | ((c >> 24) & 0x03); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x80000000) { + *cp++ = 0xfc | ((c >> 31) & 0x01); + *cp++ = 0x80 | ((c >> 24) & 0x3f); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } + } + *cp = '\0'; + return cp - dest; +} + +static VALUE +uc_tainted_str_new(SQLWCHAR *str, int len) +{ + VALUE v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } + v = rb_tainted_str_new((cp != NULL) ? cp : "", ulen); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + if (cp != NULL) { + xfree(cp); + } + return v; +} + +static VALUE +uc_tainted_str_new2(SQLWCHAR *str) +{ + return uc_tainted_str_new(str, uc_strlen(str)); +} + +static VALUE +uc_str_new(SQLWCHAR *str, int len) +{ + VALUE v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } +#ifdef USE_RB_ENC + v = rb_enc_str_new((cp != NULL) ? cp : "", ulen, rb_enc); +#else + v = rb_str_new((cp != NULL) ? cp : "", ulen); +#endif + if (cp != NULL) { + xfree(cp); + } + return v; +} + +static VALUE +uc_str_new2(SQLWCHAR *str) +{ + return uc_str_new(str, uc_strlen(str)); +} + +static VALUE +uc_str_cat(VALUE v, SQLWCHAR *str, int len) +{ + VALUE vv = v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } + if (cp != NULL) { + vv = rb_str_cat(v, cp, ulen); + xfree(cp); + } + return vv; +} + +static SQLWCHAR * +uc_from_utf(unsigned char *str, int len) +{ + SQLWCHAR *uc = NULL; + + if (str != NULL) { + int i = 0; + unsigned char *strend; + + if (len < 0) { + len = strlen((char *) str); + } + strend = str + len; + uc = ALLOC_N(SQLWCHAR, len + 1); + if (uc != NULL) { + while (str < strend) { + unsigned char c = str[0]; + + if (c < 0x80) { + uc[i++] = c; + ++str; + } else if ((c <= 0xc1) || (c >= 0xf5)) { + /* illegal, ignored */ + ++str; + } else if (c < 0xe0) { + if ((str[1] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x1f) << 6) | (str[1] & 0x3f); + + uc[i++] = t; + str += 2; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf0) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x0f) << 12) | + ((str[1] & 0x3f) << 6) | (str[2] & 0x3f); + + uc[i++] = t; + str += 3; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf8) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80) && + ((str[3] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x03) << 18) | + ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | + (str[4] & 0x3f); + + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (t >= 0x10000)) { + t -= 0x10000; + uc[i++] = 0xd800 | (t & 0x3ff); + t = 0xdc00 | ((t >> 10) & 0x3ff); + } + uc[i++] = t; + str += 4; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xfc) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80) && + ((str[3] & 0xc0) == 0x80) && + ((str[4] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x01) << 24) | + ((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) | + ((str[4] & 0x3f) << 6) | (str[5] & 0x3f); + + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (t >= 0x10000)) { + t -= 0x10000; + uc[i++] = 0xd800 | (t & 0x3ff); + t = 0xdc00 | ((t >> 10) & 0x3ff); + } + uc[i++] = t; + str += 5; + } else { + uc[i++] = c; + ++str; + } + } else { + /* ignore */ + ++str; + } + } + uc[i] = 0; + } + } + return uc; +} + +static void +uc_free(SQLWCHAR *str) +{ + if (str != NULL) { + xfree(str); + } +} + +#endif + + +/* + *---------------------------------------------------------------------- + * + * Things for ODBC::DSN + * + *---------------------------------------------------------------------- + */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +dsn_new(VALUE self) +{ + VALUE obj = rb_obj_alloc(Cdsn); + + rb_obj_call_init(obj, 0, NULL); + return obj; +} +#endif + +static VALUE +dsn_init(VALUE self) +{ + rb_iv_set(self, "@name", Qnil); + rb_iv_set(self, "@descr", Qnil); + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Things for ODBC::Driver + * + *---------------------------------------------------------------------- + */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +drv_new(VALUE self) +{ + VALUE obj = rb_obj_alloc(Cdrv); + + rb_obj_call_init(obj, 0, NULL); + return obj; +} +#endif + +static VALUE +drv_init(VALUE self) +{ + rb_iv_set(self, "@name", Qnil); + rb_iv_set(self, "@attrs", rb_hash_new()); + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Cleanup routines and GC mark/free callbacks. + * + *---------------------------------------------------------------------- + */ + +static void +list_init(LINK *link, int offs) +{ + link->succ = link->pred = link->head = NULL; + link->offs = offs; +} + +static void +list_add(LINK *link, LINK *head) +{ + if (link->head != NULL) { + rb_fatal("RubyODBC: already in list"); + } + if (head == NULL) { + rb_fatal("RubyODBC: invalid list head"); + } + link->head = head; + link->pred = NULL; + link->succ = head->succ; + head->succ = link; + if (link->succ != NULL) { + link->succ->pred = link; + } +} + +static void +list_del(LINK *link) +{ + if (link == NULL) { + rb_fatal("RubyODBC: invalid list item"); + } + if (link->head == NULL) { + rb_fatal("RubyODBC: item not in list"); + } + if (link->succ != NULL) { + link->succ->pred = link->pred; + } + if (link->pred != NULL) { + link->pred->succ = link->succ; + } else { + link->head->succ = link->succ; + } + link->succ = link->pred = link->head = NULL; +} + +static void * +list_first(LINK *head) +{ + if (head->succ == NULL) { + return NULL; + } + return (void *) ((char *) head->succ - head->offs); +} + +static int +list_empty(LINK *head) +{ + return head->succ == NULL; +} + +static void +free_env(ENV *e) +{ + e->self = Qnil; + if (!list_empty(&e->dbcs)) { + return; + } + tracemsg(2, fprintf(stderr, "ObjFree: ENV %p\n", e);); + if (e->henv != SQL_NULL_HENV) { + callsql(SQL_NULL_HENV, e->henv, SQL_NULL_HSTMT, + SQLFreeEnv(e->henv), "SQLFreeEnv"); + e->henv = SQL_NULL_HENV; + } + xfree(e); +} + +static void +link_dbc(DBC *p, ENV *e) +{ + p->envp = e; + list_add(&p->link, &e->dbcs); +} + +static void +unlink_dbc(DBC *p) +{ + if (p == NULL) { + return; + } + p->env = Qnil; + if (p->envp != NULL) { + ENV *e = p->envp; + + list_del(&p->link); + if (e->self == Qnil) { + free_env(e); + } + p->envp = NULL; + } +} + +static void +free_dbc(DBC *p) +{ + p->self = p->env = Qnil; + if (!list_empty(&p->stmts)) { + return; + } + tracemsg(2, fprintf(stderr, "ObjFree: DBC %p\n", p);); + if (p->hdbc != SQL_NULL_HDBC) { + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLDISCONNECT(p->hdbc), "SQLDisconnect"); + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLFreeConnect(p->hdbc), "SQLFreeConnect"); + p->hdbc = SQL_NULL_HDBC; + } + unlink_dbc(p); + xfree(p); +} + +static void +free_stmt_sub(STMT *q, int withp) +{ + int i; + + if (withp) { + if (q->paraminfo != NULL) { + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].outbuf != NULL) { + xfree(q->paraminfo[i].outbuf); + } + } + xfree(q->paraminfo); + q->paraminfo = NULL; + } + q->nump = 0; + } + q->ncols = 0; + if (q->coltypes != NULL) { + xfree(q->coltypes); + q->coltypes = NULL; + } + if (q->colnames != NULL) { + xfree(q->colnames); + q->colnames = NULL; + } + if (q->colvals != NULL) { + xfree(q->colvals); + q->colvals = NULL; + } + if (q->dbufs != NULL) { + xfree(q->dbufs); + q->dbufs = NULL; + } + if (q->self != Qnil) { + VALUE v; + + v = rb_iv_get(q->self, "@_a"); + if (v != Qnil) { + rb_ary_clear(v); + } + v = rb_iv_get(q->self, "@_h"); + if (v != Qnil) { + rb_iv_set(q->self, "@_h", rb_hash_new()); + } + for (i = 0; i < 4; i++) { + v = rb_iv_get(q->self, colnamebuf[i]); + if (v != Qnil) { + rb_iv_set(q->self, colnamebuf[i], rb_hash_new()); + } + } + } +} + +static void +link_stmt(STMT *q, DBC *p) +{ + q->dbcp = p; + list_add(&q->link, &p->stmts); +} + +static void +unlink_stmt(STMT *q) +{ + if (q == NULL) { + return; + } + q->dbc = Qnil; + if (q->dbcp != NULL) { + DBC *p = q->dbcp; + + list_del(&q->link); + if (p->self == Qnil) { + free_dbc(p); + } + q->dbcp = NULL; + } +} + +static void +free_stmt(STMT *q) +{ + VALUE qself = q->self; + + q->self = q->dbc = Qnil; + free_stmt_sub(q, 1); + tracemsg(2, fprintf(stderr, "ObjFree: STMT %p\n", q);); + if (q->hstmt != SQL_NULL_HSTMT) { + /* Issue warning message. */ + fprintf(stderr, "WARNING: # was not dropped" + " before garbage collection.\n", (long) qself); + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + } + unlink_stmt(q); + xfree(q); +} + +static void +start_gc() +{ + FUNCALL_NOARGS(rb_mGC, IDstart); +} + +static void +mark_dbc(DBC *p) +{ + if (p->env != Qnil) { + rb_gc_mark(p->env); + } +} + +static void +mark_stmt(STMT *q) +{ + if (q->dbc != Qnil) { + rb_gc_mark(q->dbc); + } +} + +/* + *---------------------------------------------------------------------- + * + * Set internal error (or warning) message. + * + *---------------------------------------------------------------------- + */ + +static char * +set_err(const char *msg, int warn) +{ + VALUE a, v = rb_str_new2("INTERN (0) [RubyODBC]"); + + v = rb_str_cat2(v, msg); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + a = rb_ary_new2(1); + rb_ary_push(a, rb_obj_taint(v)); + CVAR_SET(Cobj, warn ? IDatatinfo : IDataterror, a); + return STR2CSTR(v); +} + +/* + *---------------------------------------------------------------------- + * + * Functions to retrieve last SQL error or warning. + * + *---------------------------------------------------------------------- + */ + +static char * +get_err_or_info(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, int isinfo) +{ +#ifdef UNICODE + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#else + SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#endif + char buf[32], tmp[SQL_MAX_MESSAGE_LENGTH]; + SQLRETURN err; + SQLINTEGER nativeerr; + SQLSMALLINT len; + VALUE v0 = Qnil, a = Qnil, v; + int done = 0; + + while (!done) { + v = Qnil; + err = tracesql(henv, hdbc, hstmt, + SQLError(henv, hdbc, hstmt, state, &nativeerr, msg, + SQL_MAX_MESSAGE_LENGTH - 1, &len), + "SQLError"); + state[6] = '\0'; + msg[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; + switch (err) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: +#ifdef UNICODE + v = uc_str_new2(state); +#else + v = rb_str_new2((char *) state); +#endif + sprintf(buf, " (%d) ", (int) nativeerr); + v = rb_str_cat2(v, buf); +#ifdef UNICODE + v = uc_str_cat(v, msg, len); +#else + v = rb_str_cat(v, (char *) msg, len); +#endif + break; + case SQL_NO_DATA: + if ((v0 == Qnil) && (!isinfo)) { + v = rb_str_new2("INTERN (0) [RubyODBC]No data found"); + } else { + v = Qnil; + } + done = 1; + break; + case SQL_INVALID_HANDLE: + v = rb_str_new2("INTERN (0) [RubyODBC]Invalid handle"); + done = 1; + break; + case SQL_ERROR: + v = rb_str_new2("INTERN (0) [RubyODBC]Error reading error message"); + done = 1; + break; + default: + sprintf(tmp, "INTERN (0) [RubyODBC]Unknown error %d", (int) err); + v = rb_str_new2(tmp); + done = 1; + break; + } + if (v != Qnil) { + if (v0 == Qnil) { + v0 = v; + a = rb_ary_new(); + } + rb_ary_push(a, rb_obj_taint(v)); + tracemsg(1, fprintf(stderr, " | %s\n", STR2CSTR(v));); + } + } + CVAR_SET(Cobj, isinfo ? IDatatinfo : IDataterror, a); + if (isinfo) { + return NULL; + } + return (v0 == Qnil) ? NULL : STR2CSTR(v0); +} + +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) +static char * +get_installer_err() +{ +#ifdef UNICODE +#ifdef HAVE_SQLINSTALLERERRORW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH]; +#else + char msg[SQL_MAX_MESSAGE_LENGTH]; +#endif + char buf[128]; + SQLRETURN err; + VALUE v0 = Qnil, a = Qnil, v; + int done = 0; + WORD i, len; + DWORD insterrcode; + + for (i = 1; (!done) && (i <= 8); i++) { + v = Qnil; +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLInstallerErrorW", SQLInstallerErrorW); +#endif + if (have_w) { + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerErrorW(i, &insterrcode, msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerErrorW"); + msg[SQL_MAX_MESSAGE_LENGTH - 1] = 0; + } else { + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerError(i, &insterrcode, (char *) msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerErrorW"); + ((char *) msg)[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; + } +#else + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerError(i, &insterrcode, msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerError"); + msg[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; +#endif + switch (err) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + sprintf(buf, "INSTALLER (%d) ", (int) insterrcode); + v = rb_str_new2(buf); +#ifdef UNICODE + if (have_w) { +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + v = uc_str_cat(v, msg, len); + } else { + v = rb_str_cat(v, (char *) msg, len); + } +#else + v = rb_str_cat(v, msg, len); +#endif + break; + case SQL_NO_DATA: + done = 1; + break; + case SQL_ERROR: + v = rb_str_new2("INTERN (0) [RubyODBC]"); + v = rb_str_cat2(v, "Error reading installer error message"); + done = 1; + break; + default: + v = rb_str_new2("INTERN (0) [RubyODBC]"); + sprintf(buf, "Unknown installer error %d", (int) err); + v = rb_str_cat2(v, buf); + done = 1; + break; + } + if (v != Qnil) { + if (v0 == Qnil) { + v0 = v; + a = rb_ary_new(); + } + rb_ary_push(a, rb_obj_taint(v)); + tracemsg(1, fprintf(stderr, " | %s\n", STR2CSTR(v));); + } + } + CVAR_SET(Cobj, IDataterror, a); + return (v0 == Qnil) ? NULL : STR2CSTR(v0); +} +#endif + +static char * +get_err(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt) +{ + return get_err_or_info(henv, hdbc, hstmt, 0); +} + +#ifdef TRACING +static void +trace_sql_ret(SQLRETURN ret) +{ + char msg[32]; + const char *p; + + switch (ret) { + case SQL_SUCCESS: + p = "SQL_SUCCESS"; + break; + case SQL_SUCCESS_WITH_INFO: + p = "SQL_SUCCESS_WITH_INFO"; + break; + case SQL_NO_DATA: + p = "SQL_NO_DATA"; + break; + case SQL_ERROR: + p = "SQL_ERROR"; + break; + case SQL_INVALID_HANDLE: + p = "SQL_INVALID_HANDLE"; + break; + default: + sprintf(msg, "SQL_RETURN=%d", (int) ret); + p = msg; + break; + } + fprintf(stderr, " < %s\n", p); +} + +static SQLRETURN +tracesql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + const char *m) +{ + if (tracing & 1) { + fprintf(stderr, "SQLCall: %s", m); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } + return ret; +} +#endif + +static SQLRETURN +callsql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + const char *m) +{ + SQLRETURN err; + + err = tracesql(henv, hdbc, hstmt, ret, m); + if (err != SQL_SUCCESS) { +#ifdef UNICODE + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#else + SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#endif + SQLINTEGER nativeerr; + SQLSMALLINT len; + int done = 0; + + while (!done) { + err = tracesql(henv, hdbc, hstmt, + SQLError(henv, hdbc, hstmt, state, &nativeerr, msg, + SQL_MAX_MESSAGE_LENGTH - 1, &len), + "SQLError"); + switch (err) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA: + case SQL_INVALID_HANDLE: + case SQL_ERROR: + default: + done = 1; + break; + } + } + } + return ret; +} + +static int +succeeded_common(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp) +{ + if (!SQL_SUCCEEDED(ret)) { + char *dummy; + + if (msgp == NULL) { + msgp = &dummy; + } + *msgp = get_err_or_info(henv, hdbc, hstmt, 0); + return 0; + } + if (ret == SQL_SUCCESS_WITH_INFO) { + get_err_or_info(henv, hdbc, hstmt, 1); + } else { + CVAR_SET(Cobj, IDatatinfo, Qnil); + } + return 1; +} + +static int +succeeded(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp, const char *m, ...) +{ +#ifdef TRACING + va_list args; + + if (tracing & 1) { + va_start(args, m); + fprintf(stderr, "SQLCall: "); + vfprintf(stderr, m, args); + va_end(args); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } +#endif + return succeeded_common(henv, hdbc, hstmt, ret, msgp); +} + +static int +succeeded_nodata(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp, const char *m, ...) +{ +#ifdef TRACING + va_list args; + + if (tracing & 1) { + va_start(args, m); + fprintf(stderr, "SQLCall: "); + vfprintf(stderr, m, args); + va_end(args); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } +#endif + if (ret == SQL_NO_DATA) { + CVAR_SET(Cobj, IDatatinfo, Qnil); + return 1; + } + return succeeded_common(henv, hdbc, hstmt, ret, msgp); +} + +/* + *---------------------------------------------------------------------- + * + * Return ENV from VALUE. + * + *---------------------------------------------------------------------- + */ + +static VALUE +env_of(VALUE self) +{ + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + self = q->dbc; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + } + if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *p; + + Data_Get_Struct(self, DBC, p); + self = p->env; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Database", 0)); + } + } + return self; +} + +static ENV * +get_env(VALUE self) +{ + ENV *e; + + Data_Get_Struct(env_of(self), ENV, e); + return e; +} + +/* + *---------------------------------------------------------------------- + * + * Return DBC from VALUE. + * + *---------------------------------------------------------------------- + */ + +static DBC * +get_dbc(VALUE self) +{ + DBC *p; + + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + self = q->dbc; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + } + Data_Get_Struct(self, DBC, p); + return p; +} + +/* + *---------------------------------------------------------------------- + * + * Raise ODBC error from Ruby. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_raise(VALUE self, VALUE msg) +{ + VALUE a, v; + char buf[SQL_MAX_MESSAGE_LENGTH + 1], *p; + + if (TYPE(msg) == T_STRING) { + v = msg; + } else { + v = rb_any_to_s(msg); + } + strcpy(buf, "INTERN (1) [RubyODBC]"); + p = STR2CSTR(v); + strncat(buf, p, SQL_MAX_MESSAGE_LENGTH - strlen(buf)); + buf[SQL_MAX_MESSAGE_LENGTH] = '\0'; + v = rb_str_new2(buf); + a = rb_ary_new2(1); + rb_ary_push(a, rb_obj_taint(v)); + CVAR_SET(Cobj, IDataterror, a); + rb_raise(Cerror, "%s", buf); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain an ENV. + * + *---------------------------------------------------------------------- + */ + +static VALUE +env_new(VALUE self) +{ + ENV *e; + SQLHENV henv = SQL_NULL_HENV; + VALUE obj; + + if (TYPE(self) == T_MODULE) { + self = Cobj; + } + if (self == Cobj) { + self = Cenv; + } + if ((!SQL_SUCCEEDED(SQLAllocEnv(&henv))) || (henv == SQL_NULL_HENV)) { + rb_raise(Cerror, "%s", set_err("Cannot allocate SQLHENV", 0)); + } + obj = Data_Make_Struct(self, ENV, NULL, free_env, e); + tracemsg(2, fprintf(stderr, "ObjAlloc: ENV %p\n", e);); + e->self = obj; + e->henv = henv; + list_init(&e->dbcs, offsetof(DBC, link)); +#if defined(UNICODE) && defined(SQL_OV_ODBC3) + callsql(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, + (SQLPOINTER) SQL_OV_ODBC3, 0), + "SQLSetEnvAttr(SQL_OV_ODBC3)"); +#endif + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain array of known DSNs. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_dsns(VALUE self) +{ +#ifdef UNICODE + SQLWCHAR dsn[SQL_MAX_DSN_LENGTH], descr[SQL_MAX_MESSAGE_LENGTH * 2]; +#else + char dsn[SQL_MAX_DSN_LENGTH], descr[SQL_MAX_MESSAGE_LENGTH * 2]; +#endif + SQLSMALLINT dsnLen = 0, descrLen = 0; + int first = 1; + VALUE env, aret; + ENV *e; + + env = env_new(Cenv); + Data_Get_Struct(env, ENV, e); + aret = rb_ary_new(); + while (succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLDataSources(e->henv, (SQLUSMALLINT) (first ? + SQL_FETCH_FIRST : SQL_FETCH_NEXT), + (SQLTCHAR *) dsn, + (SQLSMALLINT) sizeof (dsn), &dsnLen, + (SQLTCHAR *) descr, + (SQLSMALLINT) sizeof (descr), + &descrLen), + NULL, "SQLDataSources")) { + VALUE odsn = rb_obj_alloc(Cdsn); + +#ifdef UNICODE + dsnLen = (dsnLen == 0) ? (SQLSMALLINT) uc_strlen(dsn) : + (SQLSMALLINT) (dsnLen / sizeof (SQLWCHAR)); + descrLen = (descrLen == 0) ? + (SQLSMALLINT) uc_strlen(descr) : + (SQLSMALLINT) (descrLen / sizeof (SQLWCHAR)); + rb_iv_set(odsn, "@name", uc_tainted_str_new(dsn, dsnLen)); + rb_iv_set(odsn, "@descr", uc_tainted_str_new(descr, descrLen)); +#else + dsnLen = (dsnLen == 0) ? (SQLSMALLINT) strlen(dsn) : dsnLen; + descrLen = (descrLen == 0) ? (SQLSMALLINT) strlen(descr) : descrLen; + rb_iv_set(odsn, "@name", rb_tainted_str_new(dsn, dsnLen)); + rb_iv_set(odsn, "@descr", rb_tainted_str_new(descr, descrLen)); +#endif + rb_ary_push(aret, odsn); + first = dsnLen = descrLen = 0; + } + return aret; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain array of known drivers. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_drivers(VALUE self) +{ +#ifdef UNICODE + SQLWCHAR driver[SQL_MAX_MESSAGE_LENGTH], attrs[SQL_MAX_MESSAGE_LENGTH * 2]; + SQLWCHAR *attr; +#else + char driver[SQL_MAX_MESSAGE_LENGTH], attrs[SQL_MAX_MESSAGE_LENGTH * 2]; + char *attr; +#endif + SQLSMALLINT driverLen = 0, attrsLen = 0; + int first = 1; + VALUE env, aret; + ENV *e; + + env = env_new(Cenv); + Data_Get_Struct(env, ENV, e); + aret = rb_ary_new(); + while (succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLDrivers(e->henv, (SQLUSMALLINT) (first ? + SQL_FETCH_FIRST : SQL_FETCH_NEXT), + (SQLTCHAR *) driver, + (SQLSMALLINT) sizeof (driver), &driverLen, + (SQLTCHAR *) attrs, + (SQLSMALLINT) sizeof (attrs), &attrsLen), + NULL, "SQLDrivers")) { + VALUE odrv = rb_obj_alloc(Cdrv); + VALUE h = rb_hash_new(); + int count = 0; + +#ifdef UNICODE + driverLen = (driverLen == 0) ? + (SQLSMALLINT) uc_strlen(driver) : + (SQLSMALLINT) (driverLen / sizeof (SQLWCHAR)); + rb_iv_set(odrv, "@name", uc_tainted_str_new(driver, driverLen)); + for (attr = attrs; *attr; attr += uc_strlen(attr) + 1) { + SQLWCHAR *p = uc_strchr(attr, (SQLWCHAR) '='); + + if ((p != NULL) && (p != attr)) { + rb_hash_aset(h, + uc_tainted_str_new(attr, (p - attr) / + sizeof (SQLWCHAR)), + uc_tainted_str_new2(p + 1)); + count++; + } + } +#else + driverLen = (driverLen == 0) ? (SQLSMALLINT) strlen(driver) : driverLen; + rb_iv_set(odrv, "@name", rb_tainted_str_new(driver, driverLen)); + for (attr = attrs; *attr; attr += strlen(attr) + 1) { + char *p = strchr(attr, '='); + + if ((p != NULL) && (p != attr)) { + rb_hash_aset(h, rb_tainted_str_new(attr, p - attr), + rb_tainted_str_new2(p + 1)); + count++; + } + } +#endif + if (count > 0) { + rb_iv_set(odrv, "@attrs", h); + } + rb_ary_push(aret, odrv); + first = driverLen = attrsLen = 0; + } + return aret; +} + +/* + *---------------------------------------------------------------------- + * + * Management methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_ODBCINST_H +static VALUE +conf_dsn(int argc, VALUE *argv, VALUE self, int op) +{ + VALUE drv, attr, issys, astr; +#ifdef UNICODE +#ifdef HAVE_SQLCONFIGDATASOURCEW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sdrv, *sastr; +#else + char *sdrv, *sastr; +#endif + + rb_scan_args(argc, argv, "12", &drv, &attr, &issys); + if (rb_obj_is_kind_of(drv, Cdrv) == Qtrue) { + VALUE a, x; + + if (argc > 2) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + x = rb_iv_get(drv, "@name"); + a = rb_iv_get(drv, "@attrs"); + issys = attr; + drv = x; + attr = a; + } + Check_Type(drv, T_STRING); + if (RTEST(issys)) { + switch (op) { + case ODBC_ADD_DSN: op = ODBC_ADD_SYS_DSN; break; + case ODBC_CONFIG_DSN: op = ODBC_CONFIG_SYS_DSN; break; + case ODBC_REMOVE_DSN: op = ODBC_REMOVE_SYS_DSN; break; + } + } + astr = rb_str_new2(""); + if (rb_obj_is_kind_of(attr, rb_cHash) == Qtrue) { + VALUE a, x; + + a = FUNCALL_NOARGS(attr, IDkeys); + while ((x = rb_ary_shift(a)) != Qnil) { + VALUE v = rb_hash_aref(attr, x); + + astr = rb_str_concat(astr, x); + astr = rb_str_cat2(astr, "="); + astr = rb_str_concat(astr, v); + astr = rb_str_cat(astr, "", 1); + } + } + astr = rb_str_cat(astr, "", 1); +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLConfigDataSourceW", SQLConfigDataSourceW); +#endif + if (have_w) { +#ifdef USE_RB_ENC + drv = rb_funcall(drv, IDencode, 1, rb_encv); + astr = rb_funcall(astr, IDencode, 1, rb_encv); +#endif + sdrv = uc_from_utf((unsigned char *) STR2CSTR(drv), -1); + sastr = uc_from_utf((unsigned char *) STR2CSTR(astr), -1); + if ((sdrv == NULL) || (sastr == NULL)) { + uc_free(sdrv); + uc_free(sastr); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + if (SQLConfigDataSourceW(NULL, (WORD) op, + (LPWSTR) sdrv, (LPWSTR) sastr)) { + uc_free(sdrv); + uc_free(sastr); + return Qnil; + } + uc_free(sdrv); + uc_free(sastr); + } else { + sdrv = (SQLWCHAR *) STR2CSTR(drv); + sastr = (SQLWCHAR *) STR2CSTR(astr); + if (SQLConfigDataSource(NULL, (WORD) op, + (LPCSTR) sdrv, (LPCSTR) sastr)) { + return Qnil; + } + } +#else + sdrv = STR2CSTR(drv); + sastr = STR2CSTR(astr); + if (SQLConfigDataSource(NULL, (WORD) op, sdrv, sastr)) { + return Qnil; + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("DSN configuration error", 0)); +#endif + return Qnil; +} +#endif + +static VALUE +dbc_adddsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_ADD_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::add_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_confdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_CONFIG_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::config_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_deldsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_REMOVE_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::del_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_wfdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + VALUE fname, aname, kname, val; +#ifdef UNICODE +#ifdef HAVE_SQLWRITEFILEDSNW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sfname, *saname, *skname, *sval = NULL; +#else + char *sfname, *saname, *skname, *sval = NULL; +#endif + + rb_scan_args(argc, argv, "31", &fname, &aname, &kname, &val); + Check_Type(fname, T_STRING); + Check_Type(aname, T_STRING); + Check_Type(kname, T_STRING); + if (val != Qnil) { + Check_Type(val, T_STRING); + } +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLWriteFileDSNW", SQLWriteFileDSNW); +#endif + if (have_w) { + BOOL rc; + +#ifdef USE_RB_ENC + fname = rb_funcall(fname, IDencode, 1, rb_encv); + aname = rb_funcall(aname, IDencode, 1, rb_encv); + kname = rb_funcall(kname, IDencode, 1, rb_encv); + if (val != Qnil) { + val = rb_funcall(val, IDencode, 1, rb_encv); + } +#endif + sfname = uc_from_utf((unsigned char *) STR2CSTR(fname), -1); + saname = uc_from_utf((unsigned char *) STR2CSTR(aname), -1); + skname = uc_from_utf((unsigned char *) STR2CSTR(kname), -1); + if ((sfname == NULL) || (saname == NULL) || (skname == NULL)) { +nomem: + uc_free(sfname); + uc_free(saname); + uc_free(skname); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + if (val != Qnil) { + sval = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (sval == NULL) { + goto nomem; + } + } + rc = SQLWriteFileDSNW(sfname, saname, skname, sval); + uc_free(sfname); + uc_free(saname); + uc_free(skname); + uc_free(sval); + if (rc) { + return Qnil; + } + } else { + sfname = (SQLWCHAR *) STR2CSTR(fname); + saname = (SQLWCHAR *) STR2CSTR(aname); + skname = (SQLWCHAR *) STR2CSTR(kname); + if (val != Qnil) { + sval = (SQLWCHAR *) STR2CSTR(val); + } + if (SQLWriteFileDSN((LPCSTR) sfname, (LPCSTR) saname, + (LPCSTR) skname, (LPCSTR) sval)) { + return Qnil; + } + } +#else + sfname = STR2CSTR(fname); + saname = STR2CSTR(aname); + skname = STR2CSTR(kname); + if (val != Qnil) { + sval = STR2CSTR(val); + } + if (SQLWriteFileDSN(sfname, saname, skname, sval)) { + return Qnil; + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("File DSN configuration error", 0)); +#endif +#else + rb_raise(Cerror, "%s", set_err("ODBC::write_file_dsn not supported", 0)); +#endif + return Qnil; +} + +static VALUE +dbc_rfdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + VALUE fname, aname, kname; +#ifdef UNICODE +#ifdef HAVE_SQLREADFILEDSNW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sfname, *saname, *skname, valbuf[SQL_MAX_MESSAGE_LENGTH]; +#else + char *sfname, *saname, *skname, valbuf[SQL_MAX_MESSAGE_LENGTH]; +#endif + + rb_scan_args(argc, argv, "30", &fname, &aname, &kname); + Check_Type(fname, T_STRING); + Check_Type(aname, T_STRING); + Check_Type(kname, T_STRING); +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLReadFileDSNW", SQLReadFileDSNW); +#endif + if (have_w) { + BOOL rc; + +#ifdef USE_RB_ENC + fname = rb_funcall(fname, IDencode, 1, rb_encv); + aname = rb_funcall(aname, IDencode, 1, rb_encv); + kname = rb_funcall(kname, IDencode, 1, rb_encv); +#endif + sfname = uc_from_utf((unsigned char *) STR2CSTR(fname), -1); + saname = uc_from_utf((unsigned char *) STR2CSTR(aname), -1); + skname = uc_from_utf((unsigned char *) STR2CSTR(kname), -1); + valbuf[0] = 0; + if ((sfname == NULL) || (saname == NULL) || (skname == NULL)) { + uc_free(sfname); + uc_free(saname); + uc_free(skname); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + rc = SQLReadFileDSNW(sfname, saname, skname, valbuf, + sizeof (valbuf), NULL); + uc_free(sfname); + uc_free(saname); + uc_free(skname); + if (rc) { + return uc_tainted_str_new2(valbuf); + } + } else { + sfname = (SQLWCHAR *) STR2CSTR(fname); + saname = (SQLWCHAR *) STR2CSTR(aname); + skname = (SQLWCHAR *) STR2CSTR(kname); + valbuf[0] = '\0'; + if (SQLReadFileDSN((LPCSTR) sfname, (LPCSTR) saname, + (LPCSTR) skname, (LPSTR) valbuf, + sizeof (valbuf), NULL)) { + return rb_tainted_str_new2((char *) valbuf); + } + } +#else + sfname = STR2CSTR(fname); + saname = STR2CSTR(aname); + skname = STR2CSTR(kname); + valbuf[0] = '\0'; + if (SQLReadFileDSN(sfname, saname, skname, valbuf, + sizeof (valbuf), NULL)) { + return rb_tainted_str_new2(valbuf); + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("File DSN configuration error", 0)); +#endif +#else + rb_raise(Cerror, "%s", set_err("ODBC::read_file_dsn not supported", 0)); + return Qnil; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Return last ODBC error or warning. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_error(VALUE self) +{ + return rb_cvar_get(Cobj, IDataterror); +} + +static VALUE +dbc_warn(VALUE self) +{ + return rb_cvar_get(Cobj, IDatatinfo); +} + +static VALUE +dbc_clrerror(VALUE self) +{ + CVAR_SET(Cobj, IDataterror, Qnil); + CVAR_SET(Cobj, IDatatinfo, Qnil); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Connection instance initializer. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +dbc_alloc(VALUE self) +{ + DBC *p; + VALUE obj = Data_Make_Struct(self, DBC, mark_dbc, free_dbc, p); + + tracemsg(2, fprintf(stderr, "ObjAlloc: DBC %p\n", p);); + list_init(&p->link, offsetof(DBC, link)); + p->self = obj; + p->env = Qnil; + p->envp = NULL; + list_init(&p->stmts, offsetof(STMT, link)); + p->hdbc = SQL_NULL_HDBC; + p->rbtime = Qfalse; + p->gmtime = Qfalse; + return obj; +} +#endif + +static VALUE +dbc_new(int argc, VALUE *argv, VALUE self) +{ + DBC *p; + VALUE obj, env = Qnil; + + if (TYPE(self) == T_MODULE) { + self = Cobj; + } + if (self == Cobj) { + self = Cdbc; + } + if (rb_obj_is_kind_of(self, Cenv) == Qtrue) { + env = env_of(self); + self = Cdbc; + } +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + obj = rb_obj_alloc(Cdbc); + Data_Get_Struct(obj, DBC, p); + p->env = env; +#else + obj = Data_Make_Struct(self, DBC, mark_dbc, free_dbc, p); + tracemsg(2, fprintf(stderr, "ObjAlloc: DBC %p\n", p);); + list_init(&p->link, offsetof(DBC, link)); + p->self = obj; + p->env = env; + p->envp = NULL; + list_init(&p->stmts, offsetof(STMT, link)); + p->hdbc = SQL_NULL_HDBC; + p->upc = 0; + p->use_sql_column_name = Qfalse; +#endif + if (env != Qnil) { + ENV *e; + + Data_Get_Struct(env, ENV, e); + link_dbc(p, e); + } + if (argc > 0) { + rb_obj_call_init(obj, argc, argv); + } + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Connect to data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_connect(int argc, VALUE *argv, VALUE self) +{ + ENV *e; + DBC *p; + VALUE dsn, user, passwd; +#ifdef UNICODE + SQLWCHAR *sdsn = NULL, *suser = NULL, *spasswd = NULL; +#else + char *sdsn, *suser = NULL, *spasswd = NULL; +#endif + char *msg; + SQLHDBC dbc; + + rb_scan_args(argc, argv, "03", &dsn, &user, &passwd); + if (dsn != Qnil) { + if (rb_obj_is_kind_of(dsn, Cdsn) == Qtrue) { + dsn = rb_iv_get(dsn, "@name"); + } + Check_Type(dsn, T_STRING); + } + if (user != Qnil) { + Check_Type(user, T_STRING); + } + if (passwd != Qnil) { + Check_Type(passwd, T_STRING); + } + p = get_dbc(self); + if (p->hdbc != SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("Already connected", 0)); + } + if (p->env == Qnil) { + p->env = env_new(Cenv); + e = get_env(p->env); + link_dbc(p, e); + } else { + e = get_env(p->env); + } + if (dsn == Qnil) { + return self; + } +#ifdef UNICODE + if (user != Qnil) { +#ifdef USE_RB_ENC + user = rb_funcall(user, IDencode, 1, rb_encv); +#endif + suser = uc_from_utf((unsigned char *) STR2CSTR(user), -1); + } + if (passwd != Qnil) { +#ifdef USE_RB_ENC + passwd = rb_funcall(passwd, IDencode, 1, rb_encv); +#endif + spasswd = uc_from_utf((unsigned char *) STR2CSTR(passwd), -1); + } +#ifdef USE_RB_ENC + dsn = rb_funcall(dsn, IDencode, 1, rb_encv); +#endif + sdsn = uc_from_utf((unsigned char *) STR2CSTR(dsn), -1); + if (((suser == NULL) && (user != Qnil)) || + ((spasswd == NULL) && (passwd != Qnil)) || + (sdsn == NULL)) { + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + if (user != Qnil) { + suser = STR2CSTR(user); + } + if (passwd != Qnil) { + spasswd = STR2CSTR(passwd); + } + sdsn = STR2CSTR(dsn); +#endif + if (!succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLAllocConnect(e->henv, &dbc), &msg, "SQLAllocConnect")) { +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + rb_raise(Cerror, "%s", msg); + } + if (!succeeded(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLCONNECT(dbc, (SQLTCHAR *) sdsn, SQL_NTS, + (SQLTCHAR *) suser, + (SQLSMALLINT) (suser ? SQL_NTS : 0), + (SQLTCHAR *) spasswd, + (SQLSMALLINT) (spasswd ? SQL_NTS : 0)), + &msg, + "SQLConnect('%s')", sdsn)) { +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + callsql(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLFreeConnect(dbc), "SQLFreeConnect"); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + p->hdbc = dbc; + return self; +} + +static VALUE +dbc_drvconnect(VALUE self, VALUE drv) +{ + ENV *e; + DBC *p; +#ifdef UNICODE + SQLWCHAR *sdrv; +#else + char *sdrv; +#endif + char *msg; + SQLHDBC dbc; + + if (rb_obj_is_kind_of(drv, Cdrv) == Qtrue) { + VALUE d, a, x; + + d = rb_str_new2(""); + a = FUNCALL_NOARGS(rb_iv_get(drv, "@attrs"), IDkeys); + while ((x = rb_ary_shift(a)) != Qnil) { + VALUE v = rb_hash_aref(rb_iv_get(drv, "@attrs"), x); + + d = rb_str_concat(d, x); + d = rb_str_cat2(d, "="); + d = rb_str_concat(d, v); + d = rb_str_cat2(d, ";"); + } + drv = d; + } + Check_Type(drv, T_STRING); + p = get_dbc(self); + if (p->hdbc != SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("Already connected", 0)); + } + if (p->env == Qnil) { + p->env = env_new(Cenv); + e = get_env(p->env); + link_dbc(p, e); + } else { + e = get_env(p->env); + } +#ifdef UNICODE +#ifdef USE_RB_ENC + drv = rb_funcall(drv, IDencode, 1, rb_encv); +#endif + sdrv = uc_from_utf((unsigned char *) STR2CSTR(drv), -1); + if (sdrv == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + sdrv = STR2CSTR(drv); +#endif + if (!succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLAllocConnect(e->henv, &dbc), &msg, "SQLAllocConnect")) { +#ifdef UNICODE + uc_free(sdrv); +#endif + rb_raise(Cerror, "%s", msg); + } + if (!succeeded(e->henv, dbc, SQL_NULL_HSTMT, + SQLDRIVERCONNECT(dbc, NULL, (SQLTCHAR *) sdrv, SQL_NTS, + NULL, 0, NULL, SQL_DRIVER_NOPROMPT), + &msg, "SQLDriverConnect")) { +#ifdef UNICODE + uc_free(sdrv); +#endif + callsql(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLFreeConnect(dbc), "SQLFreeConnect"); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(sdrv); +#endif + p->hdbc = dbc; + return self; +} + +static VALUE +dbc_connected(VALUE self) +{ + DBC *p = get_dbc(self); + + return (p->hdbc == SQL_NULL_HDBC) ? Qfalse : Qtrue; +} + +static VALUE +dbc_timefmt(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->rbtime = (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->rbtime; +} + +static VALUE +dbc_timeutc(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->gmtime = (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->gmtime; +} + +static VALUE +dbc_use_scn(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->use_sql_column_name = + (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->use_sql_column_name; +} + +/* + *---------------------------------------------------------------------- + * + * Drop all active statements from data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_dropall(VALUE self) +{ + DBC *p = get_dbc(self); + + while (!list_empty(&p->stmts)) { + STMT *q = list_first(&p->stmts); + + if (q->self == Qnil) { + rb_fatal("RubyODBC: invalid stmt in dropall"); + } + stmt_drop(q->self); + } + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Disconnect from data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_disconnect(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE nodrop = Qfalse; + char *msg; + + rb_scan_args(argc, argv, "01", &nodrop); + if (!RTEST(nodrop)) { + dbc_dropall(self); + } + if (p->hdbc == SQL_NULL_HDBC) { + return Qtrue; + } + if (list_empty(&p->stmts)) { + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLDISCONNECT(p->hdbc), "SQLDisconnect"); + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLFreeConnect(p->hdbc), &msg, "SQLFreeConnect")) { + rb_raise(Cerror, "%s", msg); + } + p->hdbc = SQL_NULL_HDBC; + unlink_dbc(p); + start_gc(); + return Qtrue; + } + return Qfalse; +} + +/* + *---------------------------------------------------------------------- + * + * Database meta data via SQLGetInfo() + * + *---------------------------------------------------------------------- + */ + +#ifndef SQL_DTC_TRANSITION_COST +#define SQL_DTC_TRANSITION_COST 1750 +#endif +#ifndef SQL_DTC_ENLIST_EXPENSIZE +#define SQL_DTC_ENLIST_EXPENDSIZE 1 +#endif +#ifndef SQL_DTC_UNENLIST_EXPENSIZE +#define SQL_DTC_UNENLIST_EXPENDSIZE 2 +#endif + +#define GI_CONST_SINT(x) { #x, x, SQL_C_SHORT } +#define GI_CONST_INT(x) { #x, x, SQL_C_LONG } +#define GI_CONST_BITS(x) { #x, x, SQL_C_LONG } +#define GI_CONST_STR(x) { #x, x, SQL_C_CHAR } +#define GI_CONST_END { NULL, -1, -1 } +static struct { + const char *name; + int info; + int maptype; +} get_info_map[] = { + + /* yielding ints */ + GI_CONST_SINT(SQL_ACTIVE_ENVIRONMENTS), + GI_CONST_SINT(SQL_ACTIVE_CONNECTIONS), + GI_CONST_SINT(SQL_ACTIVE_STATEMENTS), + GI_CONST_INT(SQL_ASYNC_MODE), + GI_CONST_SINT(SQL_CATALOG_LOCATION), + GI_CONST_SINT(SQL_CONCAT_NULL_BEHAVIOR), + GI_CONST_SINT(SQL_CORRELATION_NAME), + GI_CONST_SINT(SQL_CURSOR_COMMIT_BEHAVIOR), + GI_CONST_SINT(SQL_CURSOR_ROLLBACK_BEHAVIOR), + GI_CONST_INT(SQL_CURSOR_SENSITIVITY), + GI_CONST_INT(SQL_DDL_INDEX), + GI_CONST_INT(SQL_DEFAULT_TXN_ISOLATION), + GI_CONST_INT(SQL_DRIVER_HDBC), + GI_CONST_INT(SQL_DRIVER_HENV), + GI_CONST_INT(SQL_DRIVER_HDESC), + GI_CONST_INT(SQL_DRIVER_HLIB), + GI_CONST_INT(SQL_DRIVER_HSTMT), + GI_CONST_SINT(SQL_FILE_USAGE), + GI_CONST_SINT(SQL_GROUP_BY), + GI_CONST_SINT(SQL_IDENTIFIER_CASE), + GI_CONST_INT(SQL_MAX_ASYNC_CONCURRENT_STATEMENTS), + GI_CONST_INT(SQL_MAX_BINARY_LITERAL_LEN), + GI_CONST_SINT(SQL_MAX_CATALOG_NAME_LEN), + GI_CONST_INT(SQL_MAX_CHAR_LITERAL_LEN), + GI_CONST_SINT(SQL_MAX_COLUMN_NAME_LEN), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_GROUP_BY), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_INDEX), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_ORDER_BY), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_SELECT), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_TABLE), + GI_CONST_SINT(SQL_MAX_CONCURRENT_ACTIVITIES), + GI_CONST_SINT(SQL_MAX_CURSOR_NAME_LEN), + GI_CONST_SINT(SQL_MAX_DRIVER_CONNECTIONS), + GI_CONST_SINT(SQL_MAX_IDENTIFIER_LEN), + GI_CONST_INT(SQL_MAX_INDEX_SIZE), + GI_CONST_SINT(SQL_MAX_OWNER_NAME_LEN), + GI_CONST_SINT(SQL_MAX_PROCEDURE_NAME_LEN), + GI_CONST_SINT(SQL_MAX_QUALIFIER_NAME_LEN), + GI_CONST_INT(SQL_MAX_ROW_SIZE), + GI_CONST_SINT(SQL_MAX_SCHEMA_NAME_LEN), + GI_CONST_INT(SQL_MAX_STATEMENT_LEN), + GI_CONST_SINT(SQL_MAX_TABLE_NAME_LEN), + GI_CONST_SINT(SQL_MAX_TABLES_IN_SELECT), + GI_CONST_SINT(SQL_MAX_USER_NAME_LEN), + GI_CONST_SINT(SQL_NON_NULLABLE_COLUMNS), + GI_CONST_SINT(SQL_NULL_COLLATION), + GI_CONST_SINT(SQL_ODBC_API_CONFORMANCE), + GI_CONST_INT(SQL_ODBC_INTERFACE_CONFORMANCE), + GI_CONST_SINT(SQL_ODBC_SAG_CLI_CONFORMANCE), + GI_CONST_SINT(SQL_ODBC_SQL_CONFORMANCE), + GI_CONST_INT(SQL_PARAM_ARRAY_ROW_COUNTS), + GI_CONST_INT(SQL_PARAM_ARRAY_SELECTS), + GI_CONST_SINT(SQL_QUALIFIER_LOCATION), + GI_CONST_SINT(SQL_QUOTED_IDENTIFIER_CASE), + GI_CONST_INT(SQL_SQL_CONFORMANCE), + GI_CONST_SINT(SQL_TXN_CAPABLE), + + /* yielding ints (but bitmasks) */ + GI_CONST_BITS(SQL_AGGREGATE_FUNCTIONS), + GI_CONST_BITS(SQL_ALTER_DOMAIN), + GI_CONST_BITS(SQL_ALTER_TABLE), + GI_CONST_BITS(SQL_BATCH_ROW_COUNT), + GI_CONST_BITS(SQL_BATCH_SUPPORT), + GI_CONST_BITS(SQL_BOOKMARK_PERSISTENCE), + GI_CONST_BITS(SQL_CATALOG_USAGE), + GI_CONST_BITS(SQL_CONVERT_BINARY), + GI_CONST_BITS(SQL_CONVERT_BIT), + GI_CONST_BITS(SQL_CONVERT_CHAR), +#ifdef SQL_CONVERT_GUID + GI_CONST_BITS(SQL_CONVERT_GUID), +#endif + GI_CONST_BITS(SQL_CONVERT_DATE), + GI_CONST_BITS(SQL_CONVERT_DECIMAL), + GI_CONST_BITS(SQL_CONVERT_DOUBLE), + GI_CONST_BITS(SQL_CONVERT_FLOAT), + GI_CONST_BITS(SQL_CONVERT_FUNCTIONS), + GI_CONST_BITS(SQL_CONVERT_INTEGER), + GI_CONST_BITS(SQL_CONVERT_INTERVAL_YEAR_MONTH), + GI_CONST_BITS(SQL_CONVERT_INTERVAL_DAY_TIME), + GI_CONST_BITS(SQL_CONVERT_LONGVARBINARY), + GI_CONST_BITS(SQL_CONVERT_LONGVARCHAR), + GI_CONST_BITS(SQL_CONVERT_NUMERIC), + GI_CONST_BITS(SQL_CONVERT_REAL), + GI_CONST_BITS(SQL_CONVERT_SMALLINT), + GI_CONST_BITS(SQL_CONVERT_TIME), + GI_CONST_BITS(SQL_CONVERT_TIMESTAMP), + GI_CONST_BITS(SQL_CONVERT_TINYINT), + GI_CONST_BITS(SQL_CONVERT_VARBINARY), + GI_CONST_BITS(SQL_CONVERT_VARCHAR), + GI_CONST_BITS(SQL_CONVERT_WCHAR), + GI_CONST_BITS(SQL_CONVERT_WLONGVARCHAR), + GI_CONST_BITS(SQL_CONVERT_WVARCHAR), + GI_CONST_BITS(SQL_CREATE_ASSERTION), + GI_CONST_BITS(SQL_CREATE_CHARACTER_SET), + GI_CONST_BITS(SQL_CREATE_COLLATION), + GI_CONST_BITS(SQL_CREATE_DOMAIN), + GI_CONST_BITS(SQL_CREATE_SCHEMA), + GI_CONST_BITS(SQL_CREATE_TABLE), + GI_CONST_BITS(SQL_CREATE_TRANSLATION), + GI_CONST_BITS(SQL_CREATE_VIEW), + GI_CONST_BITS(SQL_DATETIME_LITERALS), + GI_CONST_BITS(SQL_DROP_ASSERTION), + GI_CONST_BITS(SQL_DROP_CHARACTER_SET), + GI_CONST_BITS(SQL_DROP_COLLATION), + GI_CONST_BITS(SQL_DROP_DOMAIN), + GI_CONST_BITS(SQL_DROP_SCHEMA), + GI_CONST_BITS(SQL_DROP_TABLE), + GI_CONST_BITS(SQL_DROP_TRANSLATION), + GI_CONST_BITS(SQL_DROP_VIEW), + GI_CONST_BITS(SQL_DTC_TRANSITION_COST), + GI_CONST_BITS(SQL_DYNAMIC_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_DYNAMIC_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_FETCH_DIRECTION), + GI_CONST_BITS(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_GETDATA_EXTENSIONS), + GI_CONST_BITS(SQL_KEYSET_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_KEYSET_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_INDEX_KEYWORDS), + GI_CONST_BITS(SQL_INFO_SCHEMA_VIEWS), + GI_CONST_BITS(SQL_INSERT_STATEMENT), + GI_CONST_BITS(SQL_LOCK_TYPES), + GI_CONST_BITS(SQL_NUMERIC_FUNCTIONS), + GI_CONST_BITS(SQL_OJ_CAPABILITIES), + GI_CONST_BITS(SQL_OWNER_USAGE), + GI_CONST_BITS(SQL_POS_OPERATIONS), + GI_CONST_BITS(SQL_POSITIONED_STATEMENTS), + GI_CONST_BITS(SQL_QUALIFIER_USAGE), + GI_CONST_BITS(SQL_SCHEMA_USAGE), + GI_CONST_BITS(SQL_SCROLL_CONCURRENCY), + GI_CONST_BITS(SQL_SCROLL_OPTIONS), + GI_CONST_BITS(SQL_SQL92_DATETIME_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_FOREIGN_KEY_DELETE_RULE), + GI_CONST_BITS(SQL_SQL92_FOREIGN_KEY_UPDATE_RULE), + GI_CONST_BITS(SQL_SQL92_GRANT), + GI_CONST_BITS(SQL_SQL92_NUMERIC_VALUE_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_PREDICATES), + GI_CONST_BITS(SQL_SQL92_RELATIONAL_JOIN_OPERATORS), + GI_CONST_BITS(SQL_SQL92_REVOKE), + GI_CONST_BITS(SQL_SQL92_ROW_VALUE_CONSTRUCTOR), + GI_CONST_BITS(SQL_SQL92_STRING_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_VALUE_EXPRESSIONS), + GI_CONST_BITS(SQL_STANDARD_CLI_CONFORMANCE), + GI_CONST_BITS(SQL_STATIC_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_STATIC_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_STATIC_SENSITIVITY), + GI_CONST_BITS(SQL_STRING_FUNCTIONS), + GI_CONST_BITS(SQL_SUBQUERIES), + GI_CONST_BITS(SQL_SYSTEM_FUNCTIONS), + GI_CONST_BITS(SQL_TIMEDATE_ADD_INTERVALS), + GI_CONST_BITS(SQL_TIMEDATE_DIFF_INTERVALS), + GI_CONST_BITS(SQL_TIMEDATE_FUNCTIONS), + GI_CONST_BITS(SQL_TXN_ISOLATION_OPTION), + GI_CONST_BITS(SQL_UNION), + + /* yielding strings */ + GI_CONST_STR(SQL_ACCESSIBLE_PROCEDURES), + GI_CONST_STR(SQL_ACCESSIBLE_TABLES), + GI_CONST_STR(SQL_CATALOG_NAME), + GI_CONST_STR(SQL_CATALOG_NAME_SEPARATOR), + GI_CONST_STR(SQL_CATALOG_TERM), + GI_CONST_STR(SQL_COLLATION_SEQ), + GI_CONST_STR(SQL_COLUMN_ALIAS), + GI_CONST_STR(SQL_DATA_SOURCE_NAME), + GI_CONST_STR(SQL_DATA_SOURCE_READ_ONLY), + GI_CONST_STR(SQL_DATABASE_NAME), + GI_CONST_STR(SQL_DBMS_NAME), + GI_CONST_STR(SQL_DBMS_VER), + GI_CONST_STR(SQL_DESCRIBE_PARAMETER), + GI_CONST_STR(SQL_DM_VER), + GI_CONST_STR(SQL_DRIVER_NAME), + GI_CONST_STR(SQL_DRIVER_ODBC_VER), + GI_CONST_STR(SQL_DRIVER_VER), + GI_CONST_STR(SQL_EXPRESSIONS_IN_ORDERBY), + GI_CONST_STR(SQL_IDENTIFIER_QUOTE_CHAR), + GI_CONST_STR(SQL_INTEGRITY), + GI_CONST_STR(SQL_KEYWORDS), + GI_CONST_STR(SQL_LIKE_ESCAPE_CLAUSE), + GI_CONST_STR(SQL_MAX_ROW_SIZE_INCLUDES_LONG), + GI_CONST_STR(SQL_MULT_RESULT_SETS), + GI_CONST_STR(SQL_MULTIPLE_ACTIVE_TXN), + GI_CONST_STR(SQL_NEED_LONG_DATA_LEN), + GI_CONST_STR(SQL_ODBC_SQL_OPT_IEF), + GI_CONST_STR(SQL_ODBC_VER), + GI_CONST_STR(SQL_ORDER_BY_COLUMNS_IN_SELECT), + GI_CONST_STR(SQL_OUTER_JOINS), + GI_CONST_STR(SQL_OWNER_TERM), + GI_CONST_STR(SQL_PROCEDURE_TERM), + GI_CONST_STR(SQL_PROCEDURES), + GI_CONST_STR(SQL_QUALIFIER_NAME_SEPARATOR), + GI_CONST_STR(SQL_QUALIFIER_TERM), + GI_CONST_STR(SQL_ROW_UPDATES), + GI_CONST_STR(SQL_SCHEMA_TERM), + GI_CONST_STR(SQL_SEARCH_PATTERN_ESCAPE), + GI_CONST_STR(SQL_SERVER_NAME), + GI_CONST_STR(SQL_SPECIAL_CHARACTERS), + GI_CONST_STR(SQL_TABLE_TERM), + GI_CONST_STR(SQL_USER_NAME), + GI_CONST_STR(SQL_XOPEN_CLI_YEAR), + + /* end of table */ + GI_CONST_END +}; + +#define GI_CONST_BITMAP(x) { #x, x } +#define GI_CONST_BITMAP_END { NULL, 0 } +static struct { + const char *name; + int bits; +} get_info_bitmap[] = { + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_AD_ADD_DOMAIN_CONSTRAINT), + GI_CONST_BITMAP(SQL_AD_ADD_DOMAIN_DEFAULT), + GI_CONST_BITMAP(SQL_AD_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_AD_DROP_DOMAIN_CONSTRAINT), + GI_CONST_BITMAP(SQL_AD_DROP_DOMAIN_DEFAULT), + GI_CONST_BITMAP(SQL_AF_ALL), + GI_CONST_BITMAP(SQL_AF_AVG), + GI_CONST_BITMAP(SQL_AF_COUNT), + GI_CONST_BITMAP(SQL_AF_DISTINCT), + GI_CONST_BITMAP(SQL_AF_MAX), + GI_CONST_BITMAP(SQL_AF_MIN), + GI_CONST_BITMAP(SQL_AF_SUM), + GI_CONST_BITMAP(SQL_AM_CONNECTION), + GI_CONST_BITMAP(SQL_AM_NONE), + GI_CONST_BITMAP(SQL_AM_STATEMENT), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_COLLATION), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_SINGLE), + GI_CONST_BITMAP(SQL_AT_ADD_CONSTRAINT), + GI_CONST_BITMAP(SQL_AT_ADD_TABLE_CONSTRAINT), +#ifdef SQL_AT_COLUMN_SINGLE + GI_CONST_BITMAP(SQL_AT_COLUMN_SINGLE), +#endif + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_CASCADE), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_RESTRICT), + GI_CONST_BITMAP(SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE), + GI_CONST_BITMAP(SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT), + GI_CONST_BITMAP(SQL_AT_SET_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_BP_CLOSE), + GI_CONST_BITMAP(SQL_BP_DELETE), + GI_CONST_BITMAP(SQL_BP_DROP), + GI_CONST_BITMAP(SQL_BP_OTHER_HSTMT), + GI_CONST_BITMAP(SQL_BP_SCROLL), + GI_CONST_BITMAP(SQL_BP_TRANSACTION), + GI_CONST_BITMAP(SQL_BP_UPDATE), + GI_CONST_BITMAP(SQL_BRC_EXPLICIT), + GI_CONST_BITMAP(SQL_BRC_PROCEDURES), + GI_CONST_BITMAP(SQL_BRC_ROLLED_UP), + GI_CONST_BITMAP(SQL_BS_ROW_COUNT_EXPLICIT), + GI_CONST_BITMAP(SQL_BS_ROW_COUNT_PROC), + GI_CONST_BITMAP(SQL_BS_SELECT_EXPLICIT), + GI_CONST_BITMAP(SQL_BS_SELECT_PROC), + GI_CONST_BITMAP(SQL_CA1_ABSOLUTE), + GI_CONST_BITMAP(SQL_CA1_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_ADD), + GI_CONST_BITMAP(SQL_CA1_BULK_DELETE_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_FETCH_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_UPDATE_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_LOCK_EXCLUSIVE), + GI_CONST_BITMAP(SQL_CA1_LOCK_NO_CHANGE), + GI_CONST_BITMAP(SQL_CA1_LOCK_UNLOCK), + GI_CONST_BITMAP(SQL_CA1_NEXT), + GI_CONST_BITMAP(SQL_CA1_POS_DELETE), + GI_CONST_BITMAP(SQL_CA1_POSITIONED_DELETE), + GI_CONST_BITMAP(SQL_CA1_POSITIONED_UPDATE), + GI_CONST_BITMAP(SQL_CA1_POS_POSITION), + GI_CONST_BITMAP(SQL_CA1_POS_REFRESH), + GI_CONST_BITMAP(SQL_CA1_POS_UPDATE), + GI_CONST_BITMAP(SQL_CA1_RELATIVE), + GI_CONST_BITMAP(SQL_CA1_SELECT_FOR_UPDATE), + GI_CONST_BITMAP(SQL_CA2_CRC_APPROXIMATE), + GI_CONST_BITMAP(SQL_CA2_CRC_EXACT), + GI_CONST_BITMAP(SQL_CA2_LOCK_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_AFFECTS_ALL), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_CATALOG), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_DELETE), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_INSERT), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_SELECT), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_UPDATE), + GI_CONST_BITMAP(SQL_CA2_OPT_ROWVER_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_OPT_VALUES_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_READ_ONLY_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_ADDITIONS), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_DELETIONS), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_UPDATES), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_NON_UNIQUE), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_TRY_UNIQUE), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_UNIQUE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CA_CREATE_ASSERTION), + GI_CONST_BITMAP(SQL_CB_CLOSE), + GI_CONST_BITMAP(SQL_CB_DELETE), + GI_CONST_BITMAP(SQL_CB_NON_NULL), + GI_CONST_BITMAP(SQL_CB_NULL), + GI_CONST_BITMAP(SQL_CB_PRESERVE), + GI_CONST_BITMAP(SQL_CC_CLOSE), + GI_CONST_BITMAP(SQL_CC_DELETE), + GI_CONST_BITMAP(SQL_CCOL_CREATE_COLLATION), + GI_CONST_BITMAP(SQL_CC_PRESERVE), + GI_CONST_BITMAP(SQL_CCS_COLLATE_CLAUSE), + GI_CONST_BITMAP(SQL_CCS_CREATE_CHARACTER_SET), + GI_CONST_BITMAP(SQL_CCS_LIMITED_COLLATION), + GI_CONST_BITMAP(SQL_CDO_COLLATION), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CDO_CREATE_DOMAIN), + GI_CONST_BITMAP(SQL_CDO_DEFAULT), + GI_CONST_BITMAP(SQL_CL_END), + GI_CONST_BITMAP(SQL_CL_START), + GI_CONST_BITMAP(SQL_CN_ANY), + GI_CONST_BITMAP(SQL_CN_DIFFERENT), + GI_CONST_BITMAP(SQL_CN_NONE), + GI_CONST_BITMAP(SQL_CONCUR_TIMESTAMP), + GI_CONST_BITMAP(SQL_CR_CLOSE), + GI_CONST_BITMAP(SQL_CR_DELETE), + GI_CONST_BITMAP(SQL_CR_PRESERVE), + GI_CONST_BITMAP(SQL_CS_AUTHORIZATION), + GI_CONST_BITMAP(SQL_CS_CREATE_SCHEMA), + GI_CONST_BITMAP(SQL_CS_DEFAULT_CHARACTER_SET), + GI_CONST_BITMAP(SQL_CT_COLUMN_COLLATION), + GI_CONST_BITMAP(SQL_CT_COLUMN_CONSTRAINT), + GI_CONST_BITMAP(SQL_CT_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_CT_COMMIT_DELETE), + GI_CONST_BITMAP(SQL_CT_COMMIT_PRESERVE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CT_CREATE_TABLE), + GI_CONST_BITMAP(SQL_CT_GLOBAL_TEMPORARY), + GI_CONST_BITMAP(SQL_CT_LOCAL_TEMPORARY), + GI_CONST_BITMAP(SQL_CTR_CREATE_TRANSLATION), + GI_CONST_BITMAP(SQL_CT_TABLE_CONSTRAINT), + GI_CONST_BITMAP(SQL_CU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_CU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_CU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_CU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_CU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_CV_CASCADED), + GI_CONST_BITMAP(SQL_CV_CHECK_OPTION), + GI_CONST_BITMAP(SQL_CV_CREATE_VIEW), + GI_CONST_BITMAP(SQL_CV_LOCAL), + GI_CONST_BITMAP(SQL_CVT_BIGINT), + GI_CONST_BITMAP(SQL_CVT_BINARY), + GI_CONST_BITMAP(SQL_CVT_BIT), + GI_CONST_BITMAP(SQL_CVT_CHAR), + GI_CONST_BITMAP(SQL_CVT_DATE), + GI_CONST_BITMAP(SQL_CVT_DECIMAL), + GI_CONST_BITMAP(SQL_CVT_DOUBLE), + GI_CONST_BITMAP(SQL_CVT_FLOAT), + GI_CONST_BITMAP(SQL_CVT_INTEGER), + GI_CONST_BITMAP(SQL_CVT_INTERVAL_DAY_TIME), + GI_CONST_BITMAP(SQL_CVT_INTERVAL_YEAR_MONTH), + GI_CONST_BITMAP(SQL_CVT_LONGVARBINARY), + GI_CONST_BITMAP(SQL_CVT_LONGVARCHAR), + GI_CONST_BITMAP(SQL_CVT_NUMERIC), + GI_CONST_BITMAP(SQL_CVT_REAL), + GI_CONST_BITMAP(SQL_CVT_SMALLINT), + GI_CONST_BITMAP(SQL_CVT_TIME), + GI_CONST_BITMAP(SQL_CVT_TIMESTAMP), + GI_CONST_BITMAP(SQL_CVT_TINYINT), + GI_CONST_BITMAP(SQL_CVT_VARBINARY), + GI_CONST_BITMAP(SQL_CVT_VARCHAR), + GI_CONST_BITMAP(SQL_CVT_WCHAR), + GI_CONST_BITMAP(SQL_CVT_WLONGVARCHAR), + GI_CONST_BITMAP(SQL_CVT_WVARCHAR), + GI_CONST_BITMAP(SQL_DA_DROP_ASSERTION), + GI_CONST_BITMAP(SQL_DC_DROP_COLLATION), + GI_CONST_BITMAP(SQL_DCS_DROP_CHARACTER_SET), + GI_CONST_BITMAP(SQL_DD_CASCADE), + GI_CONST_BITMAP(SQL_DD_DROP_DOMAIN), + GI_CONST_BITMAP(SQL_DD_RESTRICT), + GI_CONST_BITMAP(SQL_DI_CREATE_INDEX), + GI_CONST_BITMAP(SQL_DI_DROP_INDEX), + GI_CONST_BITMAP(SQL_DL_SQL92_DATE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MONTH), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_YEAR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH), + GI_CONST_BITMAP(SQL_DL_SQL92_TIME), + GI_CONST_BITMAP(SQL_DL_SQL92_TIMESTAMP), + GI_CONST_BITMAP(SQL_DS_CASCADE), + GI_CONST_BITMAP(SQL_DS_DROP_SCHEMA), + GI_CONST_BITMAP(SQL_DS_RESTRICT), + GI_CONST_BITMAP(SQL_DT_CASCADE), + GI_CONST_BITMAP(SQL_DTC_ENLIST_EXPENSIVE), + GI_CONST_BITMAP(SQL_DTC_UNENLIST_EXPENSIVE), + GI_CONST_BITMAP(SQL_DT_DROP_TABLE), + GI_CONST_BITMAP(SQL_DTR_DROP_TRANSLATION), + GI_CONST_BITMAP(SQL_DT_RESTRICT), + GI_CONST_BITMAP(SQL_DV_CASCADE), + GI_CONST_BITMAP(SQL_DV_DROP_VIEW), + GI_CONST_BITMAP(SQL_DV_RESTRICT), + GI_CONST_BITMAP(SQL_FD_FETCH_ABSOLUTE), + GI_CONST_BITMAP(SQL_FD_FETCH_BOOKMARK), + GI_CONST_BITMAP(SQL_FD_FETCH_FIRST), + GI_CONST_BITMAP(SQL_FD_FETCH_LAST), + GI_CONST_BITMAP(SQL_FD_FETCH_NEXT), + GI_CONST_BITMAP(SQL_FD_FETCH_PRIOR), + GI_CONST_BITMAP(SQL_FD_FETCH_RELATIVE), +#ifdef SQL_FD_FETCH_RESUME + GI_CONST_BITMAP(SQL_FD_FETCH_RESUME), +#endif + GI_CONST_BITMAP(SQL_FILE_CATALOG), + GI_CONST_BITMAP(SQL_FILE_NOT_SUPPORTED), + GI_CONST_BITMAP(SQL_FILE_QUALIFIER), + GI_CONST_BITMAP(SQL_FILE_TABLE), + GI_CONST_BITMAP(SQL_FN_CVT_CAST), + GI_CONST_BITMAP(SQL_FN_CVT_CONVERT), + GI_CONST_BITMAP(SQL_FN_NUM_ABS), + GI_CONST_BITMAP(SQL_FN_NUM_ACOS), + GI_CONST_BITMAP(SQL_FN_NUM_ASIN), + GI_CONST_BITMAP(SQL_FN_NUM_ATAN), + GI_CONST_BITMAP(SQL_FN_NUM_ATAN2), + GI_CONST_BITMAP(SQL_FN_NUM_CEILING), + GI_CONST_BITMAP(SQL_FN_NUM_COS), + GI_CONST_BITMAP(SQL_FN_NUM_COT), + GI_CONST_BITMAP(SQL_FN_NUM_DEGREES), + GI_CONST_BITMAP(SQL_FN_NUM_EXP), + GI_CONST_BITMAP(SQL_FN_NUM_FLOOR), + GI_CONST_BITMAP(SQL_FN_NUM_LOG), + GI_CONST_BITMAP(SQL_FN_NUM_LOG10), + GI_CONST_BITMAP(SQL_FN_NUM_MOD), + GI_CONST_BITMAP(SQL_FN_NUM_PI), + GI_CONST_BITMAP(SQL_FN_NUM_POWER), + GI_CONST_BITMAP(SQL_FN_NUM_RADIANS), + GI_CONST_BITMAP(SQL_FN_NUM_RAND), + GI_CONST_BITMAP(SQL_FN_NUM_ROUND), + GI_CONST_BITMAP(SQL_FN_NUM_SIGN), + GI_CONST_BITMAP(SQL_FN_NUM_SIN), + GI_CONST_BITMAP(SQL_FN_NUM_SQRT), + GI_CONST_BITMAP(SQL_FN_NUM_TAN), + GI_CONST_BITMAP(SQL_FN_NUM_TRUNCATE), + GI_CONST_BITMAP(SQL_FN_STR_ASCII), + GI_CONST_BITMAP(SQL_FN_STR_BIT_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CHAR), + GI_CONST_BITMAP(SQL_FN_STR_CHARACTER_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CHAR_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CONCAT), + GI_CONST_BITMAP(SQL_FN_STR_DIFFERENCE), + GI_CONST_BITMAP(SQL_FN_STR_INSERT), + GI_CONST_BITMAP(SQL_FN_STR_LCASE), + GI_CONST_BITMAP(SQL_FN_STR_LEFT), + GI_CONST_BITMAP(SQL_FN_STR_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_LOCATE), + GI_CONST_BITMAP(SQL_FN_STR_LOCATE_2), + GI_CONST_BITMAP(SQL_FN_STR_LTRIM), + GI_CONST_BITMAP(SQL_FN_STR_OCTET_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_POSITION), + GI_CONST_BITMAP(SQL_FN_STR_REPEAT), + GI_CONST_BITMAP(SQL_FN_STR_REPLACE), + GI_CONST_BITMAP(SQL_FN_STR_RIGHT), + GI_CONST_BITMAP(SQL_FN_STR_RTRIM), + GI_CONST_BITMAP(SQL_FN_STR_SOUNDEX), + GI_CONST_BITMAP(SQL_FN_STR_SPACE), + GI_CONST_BITMAP(SQL_FN_STR_SUBSTRING), + GI_CONST_BITMAP(SQL_FN_STR_UCASE), + GI_CONST_BITMAP(SQL_FN_SYS_DBNAME), + GI_CONST_BITMAP(SQL_FN_SYS_IFNULL), + GI_CONST_BITMAP(SQL_FN_SYS_USERNAME), + GI_CONST_BITMAP(SQL_FN_TD_CURDATE), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_DATE), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_TIME), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_TIMESTAMP), + GI_CONST_BITMAP(SQL_FN_TD_CURTIME), + GI_CONST_BITMAP(SQL_FN_TD_DAYNAME), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFMONTH), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFWEEK), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFYEAR), + GI_CONST_BITMAP(SQL_FN_TD_EXTRACT), + GI_CONST_BITMAP(SQL_FN_TD_HOUR), + GI_CONST_BITMAP(SQL_FN_TD_MINUTE), + GI_CONST_BITMAP(SQL_FN_TD_MONTH), + GI_CONST_BITMAP(SQL_FN_TD_MONTHNAME), + GI_CONST_BITMAP(SQL_FN_TD_NOW), + GI_CONST_BITMAP(SQL_FN_TD_QUARTER), + GI_CONST_BITMAP(SQL_FN_TD_SECOND), + GI_CONST_BITMAP(SQL_FN_TD_TIMESTAMPADD), + GI_CONST_BITMAP(SQL_FN_TD_TIMESTAMPDIFF), + GI_CONST_BITMAP(SQL_FN_TD_WEEK), + GI_CONST_BITMAP(SQL_FN_TD_YEAR), + GI_CONST_BITMAP(SQL_FN_TSI_DAY), + GI_CONST_BITMAP(SQL_FN_TSI_FRAC_SECOND), + GI_CONST_BITMAP(SQL_FN_TSI_HOUR), + GI_CONST_BITMAP(SQL_FN_TSI_MINUTE), + GI_CONST_BITMAP(SQL_FN_TSI_MONTH), + GI_CONST_BITMAP(SQL_FN_TSI_QUARTER), + GI_CONST_BITMAP(SQL_FN_TSI_SECOND), + GI_CONST_BITMAP(SQL_FN_TSI_WEEK), + GI_CONST_BITMAP(SQL_FN_TSI_YEAR), + GI_CONST_BITMAP(SQL_GB_COLLATE), + GI_CONST_BITMAP(SQL_GB_GROUP_BY_CONTAINS_SELECT), + GI_CONST_BITMAP(SQL_GB_GROUP_BY_EQUALS_SELECT), + GI_CONST_BITMAP(SQL_GB_NO_RELATION), + GI_CONST_BITMAP(SQL_GB_NOT_SUPPORTED), + GI_CONST_BITMAP(SQL_GD_ANY_COLUMN), + GI_CONST_BITMAP(SQL_GD_ANY_ORDER), + GI_CONST_BITMAP(SQL_GD_BLOCK), + GI_CONST_BITMAP(SQL_GD_BOUND), + GI_CONST_BITMAP(SQL_IC_LOWER), + GI_CONST_BITMAP(SQL_IC_MIXED), + GI_CONST_BITMAP(SQL_IC_SENSITIVE), + GI_CONST_BITMAP(SQL_IC_UPPER), + GI_CONST_BITMAP(SQL_IK_ALL), + GI_CONST_BITMAP(SQL_IK_ASC), + GI_CONST_BITMAP(SQL_IK_DESC), + GI_CONST_BITMAP(SQL_IK_NONE), + GI_CONST_BITMAP(SQL_IS_INSERT_LITERALS), + GI_CONST_BITMAP(SQL_IS_INSERT_SEARCHED), + GI_CONST_BITMAP(SQL_IS_SELECT_INTO), + GI_CONST_BITMAP(SQL_ISV_ASSERTIONS), + GI_CONST_BITMAP(SQL_ISV_CHARACTER_SETS), + GI_CONST_BITMAP(SQL_ISV_CHECK_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_COLLATIONS), + GI_CONST_BITMAP(SQL_ISV_COLUMN_DOMAIN_USAGE), + GI_CONST_BITMAP(SQL_ISV_COLUMN_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_COLUMNS), + GI_CONST_BITMAP(SQL_ISV_CONSTRAINT_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_CONSTRAINT_TABLE_USAGE), + GI_CONST_BITMAP(SQL_ISV_DOMAIN_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_DOMAINS), + GI_CONST_BITMAP(SQL_ISV_KEY_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_REFERENTIAL_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_SCHEMATA), + GI_CONST_BITMAP(SQL_ISV_SQL_LANGUAGES), + GI_CONST_BITMAP(SQL_ISV_TABLE_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_TABLE_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_TABLES), + GI_CONST_BITMAP(SQL_ISV_TRANSLATIONS), + GI_CONST_BITMAP(SQL_ISV_USAGE_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_VIEW_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_VIEWS), + GI_CONST_BITMAP(SQL_ISV_VIEW_TABLE_USAGE), + GI_CONST_BITMAP(SQL_LCK_EXCLUSIVE), + GI_CONST_BITMAP(SQL_LCK_NO_CHANGE), + GI_CONST_BITMAP(SQL_LCK_UNLOCK), + GI_CONST_BITMAP(SQL_NC_END), + GI_CONST_BITMAP(SQL_NC_HIGH), + GI_CONST_BITMAP(SQL_NC_LOW), + GI_CONST_BITMAP(SQL_NC_START), + GI_CONST_BITMAP(SQL_NNC_NON_NULL), + GI_CONST_BITMAP(SQL_NNC_NULL), + GI_CONST_BITMAP(SQL_OAC_LEVEL1), + GI_CONST_BITMAP(SQL_OAC_LEVEL2), + GI_CONST_BITMAP(SQL_OAC_NONE), + GI_CONST_BITMAP(SQL_OIC_CORE), + GI_CONST_BITMAP(SQL_OIC_LEVEL1), + GI_CONST_BITMAP(SQL_OIC_LEVEL2), + GI_CONST_BITMAP(SQL_OJ_ALL_COMPARISON_OPS), + GI_CONST_BITMAP(SQL_OJ_FULL), + GI_CONST_BITMAP(SQL_OJ_INNER), + GI_CONST_BITMAP(SQL_OJ_LEFT), + GI_CONST_BITMAP(SQL_OJ_NESTED), + GI_CONST_BITMAP(SQL_OJ_NOT_ORDERED), + GI_CONST_BITMAP(SQL_OJ_RIGHT), + GI_CONST_BITMAP(SQL_OSCC_COMPLIANT), + GI_CONST_BITMAP(SQL_OSCC_NOT_COMPLIANT), + GI_CONST_BITMAP(SQL_OSC_CORE), + GI_CONST_BITMAP(SQL_OSC_EXTENDED), + GI_CONST_BITMAP(SQL_OSC_MINIMUM), + GI_CONST_BITMAP(SQL_OU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_OU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_OU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_OU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_OU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_PARC_BATCH), + GI_CONST_BITMAP(SQL_PARC_NO_BATCH), + GI_CONST_BITMAP(SQL_PAS_BATCH), + GI_CONST_BITMAP(SQL_PAS_NO_BATCH), + GI_CONST_BITMAP(SQL_PAS_NO_SELECT), + GI_CONST_BITMAP(SQL_POS_ADD), + GI_CONST_BITMAP(SQL_POS_DELETE), + GI_CONST_BITMAP(SQL_POS_POSITION), + GI_CONST_BITMAP(SQL_POS_REFRESH), + GI_CONST_BITMAP(SQL_POS_UPDATE), + GI_CONST_BITMAP(SQL_PS_POSITIONED_DELETE), + GI_CONST_BITMAP(SQL_PS_POSITIONED_UPDATE), + GI_CONST_BITMAP(SQL_PS_SELECT_FOR_UPDATE), + GI_CONST_BITMAP(SQL_QL_END), + GI_CONST_BITMAP(SQL_QL_START), + GI_CONST_BITMAP(SQL_QU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_QU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_QU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_QU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_QU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_SCC_ISO92_CLI), + GI_CONST_BITMAP(SQL_SCCO_LOCK), + GI_CONST_BITMAP(SQL_SCCO_OPT_ROWVER), + GI_CONST_BITMAP(SQL_SCCO_OPT_TIMESTAMP), + GI_CONST_BITMAP(SQL_SCCO_OPT_VALUES), + GI_CONST_BITMAP(SQL_SCCO_READ_ONLY), + GI_CONST_BITMAP(SQL_SCC_XOPEN_CLI_VERSION1), + GI_CONST_BITMAP(SQL_SC_FIPS127_2_TRANSITIONAL), + GI_CONST_BITMAP(SQL_SC_NON_UNIQUE), + GI_CONST_BITMAP(SQL_SC_SQL92_ENTRY), + GI_CONST_BITMAP(SQL_SC_SQL92_FULL), + GI_CONST_BITMAP(SQL_SC_SQL92_INTERMEDIATE), + GI_CONST_BITMAP(SQL_SC_TRY_UNIQUE), + GI_CONST_BITMAP(SQL_SC_UNIQUE), + GI_CONST_BITMAP(SQL_SDF_CURRENT_DATE), + GI_CONST_BITMAP(SQL_SDF_CURRENT_TIME), + GI_CONST_BITMAP(SQL_SDF_CURRENT_TIMESTAMP), + GI_CONST_BITMAP(SQL_SFKD_CASCADE), + GI_CONST_BITMAP(SQL_SFKD_NO_ACTION), + GI_CONST_BITMAP(SQL_SFKD_SET_DEFAULT), + GI_CONST_BITMAP(SQL_SFKD_SET_NULL), + GI_CONST_BITMAP(SQL_SFKU_CASCADE), + GI_CONST_BITMAP(SQL_SFKU_NO_ACTION), + GI_CONST_BITMAP(SQL_SFKU_SET_DEFAULT), + GI_CONST_BITMAP(SQL_SFKU_SET_NULL), + GI_CONST_BITMAP(SQL_SG_DELETE_TABLE), + GI_CONST_BITMAP(SQL_SG_INSERT_COLUMN), + GI_CONST_BITMAP(SQL_SG_INSERT_TABLE), + GI_CONST_BITMAP(SQL_SG_REFERENCES_COLUMN), + GI_CONST_BITMAP(SQL_SG_REFERENCES_TABLE), + GI_CONST_BITMAP(SQL_SG_SELECT_TABLE), + GI_CONST_BITMAP(SQL_SG_UPDATE_COLUMN), + GI_CONST_BITMAP(SQL_SG_UPDATE_TABLE), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_CHARACTER_SET), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_COLLATION), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_DOMAIN), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_TRANSLATION), + GI_CONST_BITMAP(SQL_SG_WITH_GRANT_OPTION), + GI_CONST_BITMAP(SQL_SNVF_BIT_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_CHARACTER_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_CHAR_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_EXTRACT), + GI_CONST_BITMAP(SQL_SNVF_OCTET_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_POSITION), + GI_CONST_BITMAP(SQL_SO_DYNAMIC), + GI_CONST_BITMAP(SQL_SO_FORWARD_ONLY), + GI_CONST_BITMAP(SQL_SO_KEYSET_DRIVEN), + GI_CONST_BITMAP(SQL_SO_MIXED), + GI_CONST_BITMAP(SQL_SO_STATIC), + GI_CONST_BITMAP(SQL_SP_BETWEEN), + GI_CONST_BITMAP(SQL_SP_COMPARISON), + GI_CONST_BITMAP(SQL_SP_EXISTS), + GI_CONST_BITMAP(SQL_SP_IN), + GI_CONST_BITMAP(SQL_SP_ISNOTNULL), + GI_CONST_BITMAP(SQL_SP_ISNULL), + GI_CONST_BITMAP(SQL_SP_LIKE), + GI_CONST_BITMAP(SQL_SP_MATCH_FULL), + GI_CONST_BITMAP(SQL_SP_MATCH_PARTIAL), + GI_CONST_BITMAP(SQL_SP_MATCH_UNIQUE_FULL), + GI_CONST_BITMAP(SQL_SP_MATCH_UNIQUE_PARTIAL), + GI_CONST_BITMAP(SQL_SP_OVERLAPS), + GI_CONST_BITMAP(SQL_SP_QUANTIFIED_COMPARISON), + GI_CONST_BITMAP(SQL_SP_UNIQUE), + GI_CONST_BITMAP(SQL_SQ_COMPARISON), + GI_CONST_BITMAP(SQL_SQ_CORRELATED_SUBQUERIES), + GI_CONST_BITMAP(SQL_SQ_EXISTS), + GI_CONST_BITMAP(SQL_SQ_IN), + GI_CONST_BITMAP(SQL_SQ_QUANTIFIED), + GI_CONST_BITMAP(SQL_SR_CASCADE), + GI_CONST_BITMAP(SQL_SR_DELETE_TABLE), + GI_CONST_BITMAP(SQL_SR_GRANT_OPTION_FOR), + GI_CONST_BITMAP(SQL_SR_INSERT_COLUMN), + GI_CONST_BITMAP(SQL_SR_INSERT_TABLE), + GI_CONST_BITMAP(SQL_SRJO_CORRESPONDING_CLAUSE), + GI_CONST_BITMAP(SQL_SRJO_CROSS_JOIN), + GI_CONST_BITMAP(SQL_SRJO_EXCEPT_JOIN), + GI_CONST_BITMAP(SQL_SRJO_FULL_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_INNER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_INTERSECT_JOIN), + GI_CONST_BITMAP(SQL_SRJO_LEFT_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_NATURAL_JOIN), + GI_CONST_BITMAP(SQL_SRJO_RIGHT_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_UNION_JOIN), + GI_CONST_BITMAP(SQL_SR_REFERENCES_COLUMN), + GI_CONST_BITMAP(SQL_SR_REFERENCES_TABLE), + GI_CONST_BITMAP(SQL_SR_RESTRICT), + GI_CONST_BITMAP(SQL_SR_SELECT_TABLE), + GI_CONST_BITMAP(SQL_SR_UPDATE_COLUMN), + GI_CONST_BITMAP(SQL_SR_UPDATE_TABLE), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_CHARACTER_SET), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_COLLATION), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_DOMAIN), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_TRANSLATION), + GI_CONST_BITMAP(SQL_SRVC_DEFAULT), + GI_CONST_BITMAP(SQL_SRVC_NULL), + GI_CONST_BITMAP(SQL_SRVC_ROW_SUBQUERY), + GI_CONST_BITMAP(SQL_SRVC_VALUE_EXPRESSION), + GI_CONST_BITMAP(SQL_SS_ADDITIONS), + GI_CONST_BITMAP(SQL_SS_DELETIONS), + GI_CONST_BITMAP(SQL_SSF_CONVERT), + GI_CONST_BITMAP(SQL_SSF_LOWER), + GI_CONST_BITMAP(SQL_SSF_SUBSTRING), + GI_CONST_BITMAP(SQL_SSF_TRANSLATE), + GI_CONST_BITMAP(SQL_SSF_TRIM_BOTH), + GI_CONST_BITMAP(SQL_SSF_TRIM_LEADING), + GI_CONST_BITMAP(SQL_SSF_TRIM_TRAILING), + GI_CONST_BITMAP(SQL_SSF_UPPER), + GI_CONST_BITMAP(SQL_SS_UPDATES), + GI_CONST_BITMAP(SQL_SU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_SU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_SU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_SU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_SU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_SVE_CASE), + GI_CONST_BITMAP(SQL_SVE_CAST), + GI_CONST_BITMAP(SQL_SVE_COALESCE), + GI_CONST_BITMAP(SQL_SVE_NULLIF), + GI_CONST_BITMAP(SQL_TC_ALL), + GI_CONST_BITMAP(SQL_TC_DDL_COMMIT), + GI_CONST_BITMAP(SQL_TC_DDL_IGNORE), + GI_CONST_BITMAP(SQL_TC_DML), + GI_CONST_BITMAP(SQL_TC_NONE), + GI_CONST_BITMAP(SQL_TRANSACTION_READ_COMMITTED), + GI_CONST_BITMAP(SQL_TRANSACTION_READ_UNCOMMITTED), + GI_CONST_BITMAP(SQL_TRANSACTION_REPEATABLE_READ), + GI_CONST_BITMAP(SQL_TRANSACTION_SERIALIZABLE), + GI_CONST_BITMAP(SQL_TXN_READ_COMMITTED), + GI_CONST_BITMAP(SQL_TXN_READ_UNCOMMITTED), + GI_CONST_BITMAP(SQL_TXN_REPEATABLE_READ), + GI_CONST_BITMAP(SQL_TXN_SERIALIZABLE), +#ifdef SQL_TXN_VERSIONING + GI_CONST_BITMAP(SQL_TXN_VERSIONING), +#endif + GI_CONST_BITMAP(SQL_US_UNION), + GI_CONST_BITMAP(SQL_US_UNION_ALL), + GI_CONST_BITMAP(SQL_U_UNION), + GI_CONST_BITMAP(SQL_U_UNION_ALL), + + /* end of table */ + GI_CONST_BITMAP_END +}; + +static VALUE +dbc_getinfo(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE which, vtype, vstr; + SQLRETURN ret; + int i, k, info = -1, maptype = -1, info_found = 0; + SQLUSMALLINT sbuffer; + SQLUINTEGER lbuffer; + SQLSMALLINT len_in, len_out; + char *string = NULL, buffer[513]; + + rb_scan_args(argc, argv, "11", &which, &vtype); + switch (TYPE(which)) { + default: + vstr = rb_any_to_s(which); + string = STR2CSTR(vstr); + goto doString; + case T_STRING: + string = STR2CSTR(which); + doString: + for (i = 0; get_info_map[i].name != NULL; i++) { + if (strcmp(string, get_info_map[i].name) == 0) { + info = get_info_map[i].info; + maptype = get_info_map[i].maptype; + info_found = 2; + break; + } + } + break; + case T_FLOAT: + case T_BIGNUM: + k = (int) NUM2DBL(which); + goto doInt; + case T_FIXNUM: + k = FIX2INT(which); + doInt: + info = k; + info_found = 1; + for (i = 0; get_info_map[i].name != NULL; i++) { + if (k == get_info_map[i].info) { + info = get_info_map[i].info; + maptype = get_info_map[i].maptype; + info_found = 3; + break; + } + } + break; + } + switch (info_found) { + case 0: + rb_raise(Cerror, "%s", + set_err("Invalid info type for ODBC::Connection.get_info", + 0)); + return Qnil; + case 1: + sprintf(buffer, "Unknown info type %d for ODBC::Connection.get_info", + info); + set_err(buffer, 1); + break; + } + if (vtype != Qnil) { + switch (TYPE(vtype)) { + case T_FIXNUM: + maptype = FIX2INT(vtype); + break; + case T_BIGNUM: + case T_FLOAT: + maptype = (int) NUM2DBL(vtype); + break; + default: + rb_raise(rb_eTypeError, "need number for sql_type"); + return Qnil; + } + switch (maptype) { + case SQL_NUMERIC: + case SQL_DECIMAL: + case SQL_INTEGER: + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + case SQL_C_ULONG: +#ifdef SQL_BIGINT + case SQL_BIGINT: +#endif +#ifdef SQL_C_UBIGINT + case SQL_C_UBIGINT: +#endif + maptype = SQL_C_LONG; + break; + case SQL_SMALLINT: + case SQL_TINYINT: + case SQL_C_USHORT: + case SQL_C_UTINYINT: + maptype = SQL_C_SHORT; + break; + default: + maptype = SQL_C_CHAR; + break; + } + } + switch (maptype) { + case SQL_C_SHORT: + len_in = sizeof (sbuffer); + sbuffer = 0; + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) &sbuffer, len_in, &len_out); + break; + case SQL_C_LONG: + len_in = sizeof (lbuffer); + lbuffer = 0; + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) &lbuffer, len_in, &len_out); + break; + default: + case SQL_C_CHAR: + len_in = sizeof (buffer) - 1; + memset(buffer, 0, sizeof (buffer)); + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) buffer, len_in, &len_out); + break; + } + if (!SQL_SUCCEEDED(ret)) { + rb_raise(Cerror, "%s", + get_err(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT)); + } + switch (maptype) { + case SQL_C_SHORT: + return INT2NUM(sbuffer); + case SQL_C_LONG: + return INT2NUM(lbuffer); + default: + case SQL_C_CHAR: + return rb_str_new(buffer, len_out); + } + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Fill column type array for statement. + * + *---------------------------------------------------------------------- + */ + +static COLTYPE * +make_coltypes(SQLHSTMT hstmt, int ncols, char **msgp) +{ + int i; + COLTYPE *ret = NULL; + + for (i = 0; i < ncols; i++) { + SQLUSMALLINT ic = i + 1; + SQLLEN type = SQL_UNKNOWN_TYPE, size = 0; + + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_TYPE, NULL, 0, NULL, + &type), + msgp, "SQLColAttributes(SQL_COLUMN_TYPE)")) { + return ret; + } + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, + NULL, 0, NULL, &size), + msgp, "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)")) { + return ret; + } + } + ret = ALLOC_N(COLTYPE, ncols); + if (ret == NULL) { + if (msgp != NULL) { + *msgp = set_err("Out of memory", 0); + } + return NULL; + } + for (i = 0; i < ncols; i++) { + SQLUSMALLINT ic = i + 1; + SQLLEN type = SQL_UNKNOWN_TYPE, size = 0; + + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_TYPE, + NULL, 0, NULL, &type), + "SQLColAttributes(SQL_COLUMN_TYPE)"); + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, + NULL, 0, NULL, &size), + "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)"); + switch (type) { +#ifdef SQL_BIT + case SQL_BIT: +#endif +#ifdef SQL_TINYINT + case SQL_TINYINT: +#endif + case SQL_SMALLINT: + case SQL_INTEGER: + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + break; + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_REAL: + type = SQL_C_DOUBLE; + size = sizeof (double); + break; + case SQL_DATE: +#ifdef SQL_TYPE_DATE + case SQL_TYPE_DATE: +#endif + type = SQL_C_DATE; + size = sizeof (DATE_STRUCT); + break; + case SQL_TIME: +#ifdef SQL_TYPE_TIME + case SQL_TYPE_TIME: +#endif + type = SQL_C_TIME; + size = sizeof (TIME_STRUCT); + break; + case SQL_TIMESTAMP: +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + type = SQL_C_TIMESTAMP; + size = sizeof (TIMESTAMP_STRUCT); + break; + case SQL_LONGVARBINARY: + type = SQL_C_BINARY; + size = SQL_NO_TOTAL; + break; + case SQL_LONGVARCHAR: +#ifdef UNICODE + case SQL_WLONGVARCHAR: + type = SQL_C_WCHAR; +#else + type = SQL_C_CHAR; +#endif + size = SQL_NO_TOTAL; + break; +#ifdef SQL_BIGINT + case SQL_BIGINT: + if (sizeof (SQLBIGINT) == sizeof (SQLINTEGER)) { + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + } else { + type = SQL_C_SBIGINT; + size = sizeof (SQLBIGINT); + } + break; +#endif +#ifdef SQL_UBIGINT + case SQL_UBIGINT: + if (sizeof (SQLBIGINT) == sizeof (SQLINTEGER)) { + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + } else { + type = SQL_C_UBIGINT; + size = sizeof (SQLBIGINT); + } + break; +#endif + case SQL_DECIMAL: + case SQL_NUMERIC: + if ((size == 0) || (size > SEGSIZE)) { + size = SQL_NO_TOTAL; + } + if (size != SQL_NO_TOTAL) { + size += 2; /* sign and decimal point */ + size += 1; /* NUL terminator */ + } +#ifdef UNICODE + type = SQL_C_WCHAR; + size *= sizeof (SQLWCHAR); +#else + type = SQL_C_CHAR; +#endif + break; + default: + if ((size == 0) || (size > SEGSIZE)) { + size = SQL_NO_TOTAL; + } +#ifdef UNICODE + type = SQL_C_WCHAR; + if (size != SQL_NO_TOTAL) { + size *= sizeof (SQLWCHAR); + size += sizeof (SQLWCHAR); + } +#else + type = SQL_C_CHAR; + if (size != SQL_NO_TOTAL) { + size += 1; + } +#endif + break; + } + ret[i].type = type; + ret[i].size = size; + } + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * Fill parameter info array for statement. + * + *---------------------------------------------------------------------- + */ + +static PARAMINFO * +make_paraminfo(SQLHSTMT hstmt, int nump, char **msgp) +{ + int i; + PARAMINFO *paraminfo = NULL; + + paraminfo = ALLOC_N(PARAMINFO, nump); + if (paraminfo == NULL) { + if (msgp != NULL) { + *msgp = set_err("Out of memory", 0); + } + return NULL; + } + for (i = 0; i < nump; i++) { + paraminfo[i].iotype = SQL_PARAM_INPUT; + paraminfo[i].outsize = 0; + paraminfo[i].outbuf = NULL; + paraminfo[i].rlen = SQL_NULL_DATA; + paraminfo[i].ctype = SQL_C_CHAR; +#ifdef UNICODE + paraminfo[i].outtype = SQL_WCHAR; +#else + paraminfo[i].outtype = SQL_CHAR; +#endif + paraminfo[i].coldef_max = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLDescribeParam(hstmt, (SQLUSMALLINT) (i + 1), + ¶minfo[i].type, + ¶minfo[i].coldef, + ¶minfo[i].scale, + ¶minfo[i].nullable), + NULL, "SQLDescribeParam")) { +#ifdef UNICODE + paraminfo[i].type = SQL_WVARCHAR; +#else + paraminfo[i].type = SQL_VARCHAR; +#endif + paraminfo[i].coldef = 0; + paraminfo[i].scale = 0; + paraminfo[i].nullable = SQL_NULLABLE_UNKNOWN; + paraminfo[i].override = 0; + } + } + return paraminfo; +} + +static void +retain_paraminfo_override(STMT *q, int nump, PARAMINFO *paraminfo) +{ + if ((q->paraminfo != NULL) && (q->nump == nump)) { + int i; + + for (i = 0; i < nump; i++) { + paraminfo[i].iotype = q->paraminfo[i].iotype; + paraminfo[i].rlen = q->paraminfo[i].rlen; + paraminfo[i].ctype = q->paraminfo[i].ctype; + paraminfo[i].outtype = q->paraminfo[i].outtype; + paraminfo[i].outsize = q->paraminfo[i].outsize; + if (q->paraminfo[i].outbuf != NULL) { + paraminfo[i].outbuf = q->paraminfo[i].outbuf; + q->paraminfo[i].outbuf = NULL; + } + if (q->paraminfo[i].override) { + paraminfo[i].override = q->paraminfo[i].override; + paraminfo[i].type = q->paraminfo[i].type; + paraminfo[i].coldef = q->paraminfo[i].coldef; + paraminfo[i].scale = q->paraminfo[i].scale; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Wrap SQLHSTMT into struct/VALUE. + * + *---------------------------------------------------------------------- + */ + +static VALUE +wrap_stmt(VALUE dbc, DBC *p, SQLHSTMT hstmt, STMT **qp) +{ + VALUE stmt = Qnil; + STMT *q; + int i; + + stmt = Data_Make_Struct(Cstmt, STMT, mark_stmt, free_stmt, q); + tracemsg(2, fprintf(stderr, "ObjAlloc: STMT %p\n", q);); + list_init(&q->link, offsetof(STMT, link)); + q->self = stmt; + q->hstmt = hstmt; + q->dbc = dbc; + q->dbcp = NULL; + q->paraminfo = NULL; + q->coltypes = NULL; + q->colnames = q->dbufs = NULL; + q->colvals = NULL; + q->fetchc = 0; + q->upc = p->upc; + q->usef = 0; + rb_iv_set(q->self, "@_a", rb_ary_new()); + rb_iv_set(q->self, "@_h", rb_hash_new()); + for (i = 0; i < 4; i++) { + rb_iv_set(q->self, colnamebuf[i], rb_hash_new()); + } + if (hstmt != SQL_NULL_HSTMT) { + link_stmt(q, p); + } else { + q->dbc = Qnil; + } + if (qp != NULL) { + *qp = q; + } + return stmt; +} + +/* + *---------------------------------------------------------------------- + * + * Create statement with result. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_result(VALUE dbc, SQLHSTMT hstmt, VALUE result, int mode) +{ + DBC *p; + STMT *q; + SQLSMALLINT cols = 0, nump; + COLTYPE *coltypes = NULL; + PARAMINFO *paraminfo = NULL; + char *msg = NULL; + + Data_Get_Struct(dbc, DBC, p); + if ((hstmt == SQL_NULL_HSTMT) || + !succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLNumParams(hstmt, &nump), NULL, "SQLNumParams")) { + nump = 0; + } + if (nump > 0) { + paraminfo = make_paraminfo(hstmt, nump, &msg); + if (paraminfo == NULL) { + goto error; + } + } + if ((mode & MAKERES_PREPARE) || + (hstmt == SQL_NULL_HSTMT) || + (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLNumResultCols(hstmt, &cols), NULL, + "SQLNumResultCols"))) { + cols = 0; + } + if (cols > 0) { + coltypes = make_coltypes(hstmt, cols, &msg); + if (coltypes == NULL) { + goto error; + } + } + if (result == Qnil) { + result = wrap_stmt(dbc, p, hstmt, &q); + } else { + Data_Get_Struct(result, STMT, q); + retain_paraminfo_override(q, nump, paraminfo); + free_stmt_sub(q, 1); + if (q->dbc != dbc) { + unlink_stmt(q); + q->dbc = dbc; + if (hstmt != SQL_NULL_HSTMT) { + link_stmt(q, p); + } + } + q->hstmt = hstmt; + } + q->nump = nump; + q->paraminfo = paraminfo; + q->ncols = cols; + q->coltypes = coltypes; + if ((mode & MAKERES_BLOCK) && rb_block_given_p()) { + if (mode & MAKERES_NOCLOSE) { + return rb_yield(result); + } + return rb_ensure(rb_yield, result, stmt_close, result); + } + return result; +error: + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + if (result != Qnil) { + Data_Get_Struct(result, STMT, q); + if (q->hstmt == hstmt) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + } + if (paraminfo != NULL) { + xfree(paraminfo); + } + if (coltypes != NULL) { + xfree(coltypes); + } + rb_raise(Cerror, "%s", msg); + return Qnil; +} + +static char * +upcase_if(char *string, int upc) +{ + if (upc && (string != NULL)) { + unsigned char *p = (unsigned char *) string; + + while (*p != '\0') { +#ifdef UNICODE + if ((*p < 0x80) && ISLOWER(*p)) +#else + if (ISLOWER(*p)) +#endif + { + *p = toupper(*p); + } + ++p; + } + } + return string; +} + +/* + *---------------------------------------------------------------------- + * + * Constructor: make column from statement. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_column(SQLHSTMT hstmt, int i, int upc, int use_scn) +{ + VALUE obj, v; + SQLUSMALLINT ic = i + 1; + SQLLEN iv; +#ifdef UNICODE + SQLWCHAR name[SQL_MAX_MESSAGE_LENGTH]; +#else + char name[SQL_MAX_MESSAGE_LENGTH]; +#endif + char *msg; + SQLSMALLINT name_len; + + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + (SQLSMALLINT) sizeof (name), + &name_len, NULL), + &msg, use_scn ? "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)")) { + rb_raise(Cerror, "%s", msg); + } + obj = rb_obj_alloc(Ccolumn); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + if (upc) { + int len = uc_strlen(name); + char tmpbuf[1]; + char *tmp = xmalloc(len); + VALUE v; + + if (tmp == NULL) { + tmp = tmpbuf; + len = 0; + } + mkutf(tmp, name, len); + v = rb_tainted_str_new2(upcase_if(tmp, 1)); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + rb_iv_set(obj, "@name", v); + if ((tmp != NULL) && (tmp != tmpbuf)) { + xfree(tmp); + } + } else { + rb_iv_set(obj, "@name", uc_tainted_str_new2(name)); + } +#else + rb_iv_set(obj, "@name", rb_tainted_str_new2(upcase_if(name, upc))); +#endif + v = Qnil; + name[0] = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_TABLE_NAME, name, + (SQLSMALLINT) sizeof (name), + &name_len, NULL), + NULL, "SQLColAttributes(SQL_COLUMN_TABLE_NAME)")) { + if (name_len > (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + v = uc_tainted_str_new2(name); +#else + v = rb_tainted_str_new2(name); +#endif + } + rb_iv_set(obj, "@table", v); + iv = SQL_UNKNOWN_TYPE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_TYPE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_TYPE)")) { + v = INT2NUM(iv); + } else { + v = INT2NUM(SQL_UNKNOWN_TYPE); + } + rb_iv_set(obj, "@type", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, +#if (ODBCVER >= 0x0300) + SQL_DESC_LENGTH, +#else + SQL_COLUMN_LENGTH, +#endif + NULL, 0, NULL, &iv), + NULL, +#if (ODBCVER >= 0x0300) + "SQLColAttributes(SQL_DESC_LENGTH)" +#else + "SQLColAttributes(SQL_COLUMN_LENGTH)" +#endif + )) { + v = INT2NUM(iv); + } else if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@length", v); + v = Qnil; + iv = SQL_NO_NULLS; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_NULLABLE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_NULLABLE)")) { + v = (iv == SQL_NO_NULLS) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@nullable", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_SCALE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_SCALE)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@scale", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_PRECISION, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_PRECISION)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@precision", v); + v = Qnil; + iv = SQL_UNSEARCHABLE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_SEARCHABLE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_SEARCHABLE)")) { + v = (iv == SQL_UNSEARCHABLE) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@searchable", v); + v = Qnil; + iv = SQL_FALSE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_UNSIGNED, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_UNSIGNED)")) { + v = (iv == SQL_FALSE) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@unsigned", v); + v = Qnil; +#ifdef SQL_COLUMN_AUTO_INCREMENT + iv = SQL_FALSE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_AUTO_INCREMENT, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_AUTO_INCREMENT)")) { + v = (iv == SQL_FALSE) ? Qfalse : Qtrue; + } +#endif + rb_iv_set(obj, "@autoincrement", v); + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Constructor: make parameter from statement. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_param(STMT *q, int i) +{ + VALUE obj; + int v; + + obj = rb_obj_alloc(Cparam); +#ifdef UNICODE + v = q->paraminfo ? q->paraminfo[i].type : SQL_WVARCHAR; +#else + v = q->paraminfo ? q->paraminfo[i].type : SQL_VARCHAR; +#endif + rb_iv_set(obj, "@type", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].coldef : 0; + rb_iv_set(obj, "@precision", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].scale : 0; + rb_iv_set(obj, "@scale", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].nullable : SQL_NULLABLE_UNKNOWN; + rb_iv_set(obj, "@nullable", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].iotype : SQL_PARAM_INPUT; + rb_iv_set(obj, "@iotype", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].outsize : 0; + rb_iv_set(obj, "@output_size", INT2NUM(v)); +#ifdef UNICODE + v = q->paraminfo ? q->paraminfo[i].outtype : SQL_WCHAR; +#else + v = q->paraminfo ? q->paraminfo[i].outtype : SQL_CHAR; +#endif + rb_iv_set(obj, "@output_type", INT2NUM(v)); + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Query tables/columns/keys/indexes/types of data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_info(int argc, VALUE *argv, VALUE self, int mode) +{ + DBC *p = get_dbc(self); + VALUE which = Qnil, which2 = Qnil, which3 = Qnil; +#ifdef UNICODE + SQLWCHAR *swhich = NULL, *swhich2 = NULL; +#else + SQLCHAR *swhich = NULL, *swhich2 = NULL; +#endif + char *msg; + const char *argspec = NULL; + SQLHSTMT hstmt; + int needstr = 1, itype = SQL_ALL_TYPES; + int iid = SQL_BEST_ROWID, iscope = SQL_SCOPE_CURROW; + + if (p->hdbc == SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("No connection", 0)); + } + switch (mode) { + case INFO_TYPES: + needstr = 0; + /* FALL THRU */ + case INFO_TABLES: + case INFO_PRIMKEYS: + case INFO_TPRIV: + case INFO_PROCS: + argspec = "01"; + break; + case INFO_COLUMNS: + argspec = "02"; + break; + case INFO_INDEXES: + case INFO_FORKEYS: + case INFO_PROCCOLS: + argspec = "11"; + break; + case INFO_SPECCOLS: + argspec = "12"; + break; + default: + rb_raise(Cerror, "%s", set_err("Invalid info mode", 0)); + break; + } + rb_scan_args(argc, argv, argspec, &which, &which2, &which3); + if (which != Qnil) { + if (needstr) { + Check_Type(which, T_STRING); +#ifdef UNICODE + swhich = (SQLWCHAR *) which; +#else + swhich = (SQLCHAR *) STR2CSTR(which); +#endif + } else { + itype = NUM2INT(which); + } + } + if (which2 != Qnil) { + if (mode == INFO_SPECCOLS) { + iid = NUM2INT(which2); + } else if (mode != INFO_INDEXES) { + Check_Type(which2, T_STRING); +#ifdef UNICODE + swhich2 = (SQLWCHAR *) which2; +#else + swhich2 = (SQLCHAR *) STR2CSTR(which2); +#endif + } + } +#ifdef UNICODE + if (swhich != NULL) { + VALUE val = (VALUE) swhich; + +#ifdef USE_RB_ENC + val = rb_funcall(val, IDencode, 1, rb_encv); +#endif + swhich = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (swhich == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + } + if (swhich2 != NULL) { + VALUE val = (VALUE) swhich2; + +#ifdef USE_RB_ENC + val = rb_funcall(val, IDencode, 1, rb_encv); +#endif + swhich2 = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (swhich2 == NULL) { + uc_free(swhich); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + } +#endif + if (which3 != Qnil) { + iscope = NUM2INT(which3); + } + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), &msg, "SQLAllocStmt")) { +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + rb_raise(Cerror, "%s", msg); + } + switch (mode) { + case INFO_TABLES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLTABLES(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + NULL, 0), + &msg, "SQLTables")) { + goto error; + } + break; + case INFO_COLUMNS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLCOLUMNS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + swhich2, (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLColumns")) { + goto error; + } + break; + case INFO_PRIMKEYS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPRIMARYKEYS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLPrimaryKeys")) { + goto error; + } + break; + case INFO_INDEXES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLSTATISTICS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + (SQLUSMALLINT) (RTEST(which2) ? + SQL_INDEX_UNIQUE : SQL_INDEX_ALL), + SQL_ENSURE), + &msg, "SQLStatistics")) { + goto error; + } + break; + case INFO_TYPES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLGETTYPEINFO(hstmt, (SQLSMALLINT) itype), + &msg, "SQLGetTypeInfo")) { + goto error; + } + break; + case INFO_FORKEYS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFOREIGNKEYS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + NULL, 0, NULL, 0, + swhich2, (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLForeignKeys")) { + goto error; + } + break; + case INFO_TPRIV: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLTABLEPRIVILEGES(hstmt, NULL, 0, NULL, 0, swhich, + (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLTablePrivileges")) { + goto error; + } + break; + case INFO_PROCS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPROCEDURES(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLProcedures")) { + goto error; + } + break; + case INFO_PROCCOLS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPROCEDURECOLUMNS(hstmt, NULL, 0, NULL, 0, + swhich, + (swhich == NULL) ? 0 : SQL_NTS, + swhich2, + (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLProcedureColumns")) { + goto error; + } + break; + case INFO_SPECCOLS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLSPECIALCOLUMNS(hstmt, (SQLUSMALLINT) iid, + NULL, 0, NULL, 0, + swhich, + (swhich == NULL) ? 0 : SQL_NTS, + (SQLUSMALLINT) iscope, + SQL_NULLABLE), + &msg, "SQLSpecialColumns")) { + goto error; + } + break; + } +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + return make_result(self, hstmt, Qnil, MAKERES_BLOCK); +error: +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + rb_raise(Cerror, "%s", msg); + return Qnil; +} + +static VALUE +dbc_tables(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TABLES); +} + +static VALUE +dbc_columns(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_COLUMNS); +} + +static VALUE +dbc_primkeys(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PRIMKEYS); +} + +static VALUE +dbc_indexes(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_INDEXES); +} + +static VALUE +dbc_types(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TYPES); +} + +static VALUE +dbc_forkeys(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_FORKEYS); +} + +static VALUE +dbc_tpriv(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TPRIV); +} + +static VALUE +dbc_procs(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PROCS); +} + +static VALUE +dbc_proccols(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PROCCOLS); +} + +static VALUE +dbc_speccols(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_SPECCOLS); +} + +/* + *---------------------------------------------------------------------- + * + * Transaction stuff. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_trans(VALUE self, int what) +{ + ENV *e; + SQLHDBC dbc = SQL_NULL_HDBC; + SQLRETURN ret; + char *msg; + + e = get_env(self); + if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *d; + + d = get_dbc(self); + dbc = d->hdbc; + } +#if (ODBCVER >= 0x0300) + ret = SQLENDTRAN((SQLSMALLINT) + ((dbc == SQL_NULL_HDBC) ? SQL_HANDLE_ENV : SQL_HANDLE_DBC), + (dbc == SQL_NULL_HDBC) ? e->henv : dbc, + (SQLSMALLINT) what); +#else + ret = SQLTRANSACT(e->henv, dbc, (SQLUSMALLINT) what); +#endif + if (!succeeded(e->henv, dbc, SQL_NULL_HSTMT, ret, +#if (ODBCVER >= 0x0300) + &msg, "SQLEndTran" +#else + &msg, "SQLTransact" +#endif + )) { + rb_raise(Cerror, "%s", msg); + } + return Qnil; +} + +static VALUE +dbc_commit(VALUE self) +{ + return dbc_trans(self, SQL_COMMIT); +} + +static VALUE +dbc_rollback(VALUE self) +{ + return dbc_trans(self, SQL_ROLLBACK); +} + +static VALUE +dbc_nop(VALUE self) +{ + return Qnil; +} + +static VALUE +dbc_transbody(VALUE self) +{ + return rb_yield(rb_ary_entry(self, 0)); +} + +static VALUE +dbc_transfail(VALUE self, VALUE err) +{ + rb_ary_store(self, 1, err); + dbc_rollback(rb_ary_entry(self, 0)); + return Qundef; +} + +static VALUE +dbc_transaction(VALUE self) +{ + VALUE a, ret; + + if (!rb_block_given_p()) { + rb_raise(rb_eArgError, "block required"); + } + rb_ensure(dbc_commit, self, dbc_nop, self); + a = rb_ary_new2(2); + rb_ary_store(a, 0, self); + rb_ary_store(a, 1, Qnil); + if ((ret = rb_rescue2(dbc_transbody, a, dbc_transfail, a, + rb_eException, (VALUE) 0)) != Qundef) { + dbc_commit(self); + return ret; + } + ret = rb_ary_entry(a, 1); + rb_exc_raise(rb_exc_new3(rb_obj_class(ret), + FUNCALL_NOARGS(ret, IDto_s))); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Environment attribute handling. + * + *---------------------------------------------------------------------- + */ + +#if (ODBCVER >= 0x0300) +static VALUE +do_attr(int argc, VALUE *argv, VALUE self, int op) +{ + SQLHENV henv = SQL_NULL_HENV; + VALUE val; + SQLLEN v = 0; + SQLPOINTER vp; + SQLINTEGER l; + char *msg; + + if (self != Modbc) { + henv = get_env(self)->henv; + } + rb_scan_args(argc, argv, "01", &val); + if (val == Qnil) { + if (!succeeded(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLGetEnvAttr(henv, (SQLINTEGER) op, + (SQLPOINTER) &v, sizeof (v), &l), + &msg, "SQLGetEnvAttr(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + return rb_int2inum(v); + } + v = NUM2INT(val); + vp = (SQLPOINTER) v; + if (!succeeded(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLSetEnvAttr(henv, (SQLINTEGER) op, vp, SQL_IS_INTEGER), + &msg, "SQLSetEnvAttr(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + return Qnil; +} +#endif + +static VALUE +env_cpooling(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_CONNECTION_POOLING); +#else + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); + return Qnil; +#endif +} + +static VALUE +env_cpmatch(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_CP_MATCH); +#else + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); + return Qnil; +#endif +} + +static VALUE +env_odbcver(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_ODBC_VERSION); +#else + VALUE val; + + rb_scan_args(argc, argv, "01", &val); + if (val == Qnil) { + return rb_int2inum(ODBCVER >> 8); + } + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Connection/statement option handling. + * + * Note: + * ODBC 2 allows statement options to be set using SQLSetConnectOption, + * establishing the statement option as a default for any hstmts + * later allocated for that hdbc. This feature was deprecated in + * ODBC 3.x and may not work with ODBC 3.x drivers. + * + * Although the Database class includes attribute accessors for + * statement-level options, a safer alternative, if using an ODBC 3 + * driver, is to set the option directly on the Statement instance. + * + *---------------------------------------------------------------------- + */ + +#define OPT_LEVEL_STMT 1 +#define OPT_LEVEL_DBC 2 +#define OPT_LEVEL_BOTH (OPT_LEVEL_STMT | OPT_LEVEL_DBC) + +#define OPT_CONST_INT(x, level) { #x, x, level } +#define OPT_CONST_END { NULL, -1 } +static struct { + const char *name; + int option; + int level; +} option_map[] = { + + /* yielding ints */ + OPT_CONST_INT(SQL_AUTOCOMMIT, OPT_LEVEL_DBC), + OPT_CONST_INT(SQL_LOGIN_TIMEOUT, OPT_LEVEL_DBC), + OPT_CONST_INT(SQL_NOSCAN, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_CONCURRENCY, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_QUERY_TIMEOUT, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_MAX_ROWS, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_MAX_LENGTH, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_ROWSET_SIZE, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_CURSOR_TYPE, OPT_LEVEL_BOTH), + + /* end of table */ + OPT_CONST_END +}; + +static VALUE +do_option(int argc, VALUE *argv, VALUE self, int isstmt, int op) +{ + DBC *p = NULL; + STMT *q = NULL; + VALUE val, val2 = Qnil; + SQLINTEGER v; + char *msg; + int level = isstmt ? OPT_LEVEL_STMT : OPT_LEVEL_DBC; + + if (op == -1) { + rb_scan_args(argc, argv, "11", &val, &val2); + } else { + rb_scan_args(argc, argv, "01", &val); + } + if (isstmt) { + Data_Get_Struct(self, STMT, q); + if (q->dbc == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + if (q->hstmt == SQL_NULL_HSTMT) { + rb_raise(Cerror, "%s", set_err("No statement", 0)); + } + } else { + p = get_dbc(self); + if (p->hdbc == SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("No connection", 0)); + } + } + if (op == -1) { + VALUE vstr; + char *string; + int i, op_found = 0; + + switch (TYPE(val)) { + default: + vstr = rb_any_to_s(val); + string = STR2CSTR(vstr); + goto doString; + case T_STRING: + string = STR2CSTR(val); + doString: + for (i = 0; option_map[i].name != NULL; i++) { + if (strcmp(string, option_map[i].name) == 0) { + op = option_map[i].option; + level = option_map[i].level; + op_found = 3; + break; + } + } + break; + case T_FLOAT: + case T_BIGNUM: + op = (int) NUM2DBL(val); + goto doInt; + case T_FIXNUM: + op = FIX2INT(val); + doInt: + op_found = 1; + for (i = 0; option_map[i].name != NULL; i++) { + if (op == option_map[i].option) { + level = option_map[i].level; + op_found = 2; + break; + } + } + break; + } + if (!op_found) { + rb_raise(Cerror, "%s", set_err("Unknown option", 0)); + return Qnil; + } + val = val2; + } + if ((isstmt && (!(level & OPT_LEVEL_STMT))) || + (!isstmt && (!(level & OPT_LEVEL_DBC)))) { + rb_raise(Cerror, "%s", + set_err("Invalid option type for this level", 0)); + return Qnil; + } + if (val == Qnil) { + if (p != NULL) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLGetConnectOption(p->hdbc, (SQLUSMALLINT) op, + (SQLPOINTER) &v), + &msg, "SQLGetConnectOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } else { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HSTMT, q->hstmt, + SQLGetStmtOption(q->hstmt, (SQLUSMALLINT) op, + (SQLPOINTER) &v), + &msg, "SQLGetStmtOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } + } + switch (op) { + case SQL_AUTOCOMMIT: + if (val == Qnil) { + return v ? Qtrue : Qfalse; + } + v = (TYPE(val) == T_FIXNUM) ? + (FIX2INT(val) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF) : + (RTEST(val) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); + break; + + case SQL_NOSCAN: + if (val == Qnil) { + return v ? Qtrue : Qfalse; + } + v = (TYPE(val) == T_FIXNUM) ? + (FIX2INT(val) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF) : + (RTEST(val) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF); + break; + + case SQL_LOGIN_TIMEOUT: + case SQL_CONCURRENCY: + case SQL_QUERY_TIMEOUT: + case SQL_MAX_ROWS: + case SQL_MAX_LENGTH: + case SQL_ROWSET_SIZE: + case SQL_CURSOR_TYPE: + default: + if (val == Qnil) { + return rb_int2inum(v); + } + Check_Type(val, T_FIXNUM); + v = FIX2INT(val); + if (op == SQL_ROWSET_SIZE) { + rb_raise(Cerror, "%s", set_err("Read only attribute", 0)); + } + break; + } + if (p != NULL) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLSetConnectOption(p->hdbc, (SQLUSMALLINT) op, + (SQLUINTEGER) v), + &msg, "SQLSetConnectOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } else { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLSetStmtOption(q->hstmt, (SQLUSMALLINT) op, + (SQLUINTEGER) v), + &msg, "SQLSetStmtOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } + return Qnil; +} + +static VALUE +dbc_autocommit(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_AUTOCOMMIT); +} + +static VALUE +dbc_concurrency(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_CONCURRENCY); +} + +static VALUE +dbc_maxrows(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_MAX_ROWS); +} + +static VALUE +dbc_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_QUERY_TIMEOUT); +} + +static VALUE +dbc_login_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_LOGIN_TIMEOUT); +} + +static VALUE +dbc_maxlength(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_MAX_LENGTH); +} + +static VALUE +dbc_rowsetsize(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_ROWSET_SIZE); +} + +static VALUE +dbc_cursortype(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_CURSOR_TYPE); +} + +static VALUE +dbc_noscan(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_NOSCAN); +} + +static VALUE +dbc_getsetoption(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, -1); +} + +static VALUE +stmt_concurrency(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_CONCURRENCY); +} + +static VALUE +stmt_maxrows(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_MAX_ROWS); +} + +static VALUE +stmt_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_QUERY_TIMEOUT); +} + +static VALUE +stmt_maxlength(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_MAX_LENGTH); +} + +static VALUE +stmt_rowsetsize(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_ROWSET_SIZE); +} + +static VALUE +stmt_cursortype(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_CURSOR_TYPE); +} + +static VALUE +stmt_noscan(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_NOSCAN); +} + +static VALUE +stmt_getsetoption(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, -1); +} + +/* + *---------------------------------------------------------------------- + * + * Scan literal date/time/timestamp to TIMESTAMP_STRUCT. + * + *---------------------------------------------------------------------- + */ + +static int +scan_dtts(VALUE str, int do_d, int do_t, TIMESTAMP_STRUCT *ts) +{ + int yy = 0, mm = 0, dd = 0, hh = 0, mmm = 0, ss = 0, ff = 0, i; + char c, *cstr = STR2CSTR(str); + + memset(ts, 0, sizeof (TIMESTAMP_STRUCT)); + if (((sscanf(cstr, "{ts '%d-%d-%d %d:%d:%d.%d' %c", + &yy, &mm, &dd, &hh, &mmm, &ss, &ff, &c) == 8) || + (sscanf(cstr, "{ts '%d-%d-%d %d:%d:%d' %c", + &yy, &mm, &dd, &hh, &mmm, &ss, &c) == 7)) && + (c == '}')) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + ts->fraction = ff; + return 1; + } + if (do_d && + (sscanf(cstr, "{d '%d-%d-%d' %c", &yy, &mm, &dd, &c) == 4) && + (c == '}')) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + return 1; + } + if (do_t && + (sscanf(cstr, "{t '%d:%d:%d' %c", &hh, &mmm, &ss, &c) == 4) && + (c == '}')) { + ts->hour = yy; + ts->minute = mmm; + ts->second = ss; + return 1; + } + ff = ss = 0; + i = sscanf(cstr, "%d-%d-%d %d:%d:%d%c%d", + &yy, &mm, &dd, &hh, &mmm, &ss, &c, &ff); + if (i >= 5) { + if ((i > 6) && (c != 0) && (strchr(". \t", c) == NULL)) { + goto next; + } + ts->year = yy; + ts->month = mm; + ts->day = dd; + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + ts->fraction = ff; + return 1; + } +next: + ff = ss = 0; + if (do_d && (sscanf(cstr, "%d-%d-%d", &yy, &mm, &dd) == 3)) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + return 1; + } + if (do_t && (sscanf(cstr, "%d:%d:%d", &hh, &mmm, &ss) == 3)) { + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + return 1; + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * Date methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +date_alloc(VALUE self) +{ + DATE_STRUCT *date; + VALUE obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + + memset(date, 0, sizeof (*date)); + return obj; +} +#else +static VALUE +date_new(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +date_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, 1, 0, &tss)) { + DATE_STRUCT *date; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + } else { + obj = self; + Data_Get_Struct(self, DATE_STRUCT, date); + } + date->year = tss.year; + date->month = tss.month; + date->day = tss.day; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::Date format error"); + } + return Qnil; +} + +static VALUE +date_load(VALUE self, VALUE str) +{ + return date_load1(self, str, 1); +} + +static VALUE +date_init(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE d, m, y; + + rb_scan_args(argc, argv, "03", &y, &m, &d); + if (rb_obj_is_kind_of(y, Cdate) == Qtrue) { + DATE_STRUCT *date2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, DATE_STRUCT, date); + Data_Get_Struct(y, DATE_STRUCT, date2); + *date = *date2; + return self; + } + if (rb_obj_is_kind_of(y, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, DATE_STRUCT, date); + Data_Get_Struct(y, TIMESTAMP_STRUCT, ts); + date->year = ts->year; + date->month = ts->month; + date->day = ts->day; + return self; + } + if (rb_obj_is_kind_of(y, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + d = FUNCALL_NOARGS(y, IDday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if (rb_obj_is_kind_of(y, rb_cDate) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + d = FUNCALL_NOARGS(y, IDmday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if ((argc == 1) && (rb_obj_is_kind_of(y, rb_cString) == Qtrue)) { + if (date_load1(self, y, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, DATE_STRUCT, date); + date->year = (y == Qnil) ? 0 : NUM2INT(y); + date->month = (m == Qnil) ? 0 : NUM2INT(m); + date->day = (d == Qnil) ? 0 : NUM2INT(d); + return self; +} + +static VALUE +date_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + DATE_STRUCT *date1, *date2; + + Data_Get_Struct(self, DATE_STRUCT, date1); + Data_Get_Struct(obj, DATE_STRUCT, date2); + *date2 = *date1; + return obj; +#else + return date_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +date_to_s(VALUE self) +{ + DATE_STRUCT *date; + char buf[128]; + + Data_Get_Struct(self, DATE_STRUCT, date); + sprintf(buf, "%04d-%02d-%02d", date->year, date->month, date->day); + return rb_str_new2(buf); +} + +static VALUE +date_dump(VALUE self, VALUE depth) +{ + return date_to_s(self); +} + +static VALUE +date_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +date_year(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->year); + } + date->year = NUM2INT(v); + return self; +} + +static VALUE +date_month(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->month); + } + date->month = NUM2INT(v); + return self; +} + +static VALUE +date_day(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->day); + } + date->day = NUM2INT(v); + return self; +} + +static VALUE +date_cmp(VALUE self, VALUE date) +{ + DATE_STRUCT *date1, *date2; + + if (rb_obj_is_kind_of(date, Cdate) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Date as argument"); + } + Data_Get_Struct(self, DATE_STRUCT, date1); + Data_Get_Struct(date, DATE_STRUCT, date2); + if (date1->year < date2->year) { + return INT2FIX(-1); + } + if (date1->year == date2->year) { + if (date1->month < date2->month) { + return INT2FIX(-1); + } + if (date1->month == date2->month) { + if (date1->day < date2->day) { + return INT2FIX(-1); + } + if (date1->day == date2->day) { + return INT2FIX(0); + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * Time methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +time_alloc(VALUE self) +{ + TIME_STRUCT *time; + VALUE obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + + memset(time, 0, sizeof (*time)); + return obj; +} +#else +static VALUE +time_new(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +time_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, 0, 1, &tss)) { + TIME_STRUCT *time; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + } else { + obj = self; + Data_Get_Struct(self, TIME_STRUCT, time); + } + time->hour = tss.hour; + time->minute = tss.minute; + time->second = tss.second; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::Time format error"); + } + return Qnil; +} + +static VALUE +time_load(VALUE self, VALUE str) +{ + return time_load1(self, str, 1); +} + +static VALUE +time_init(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE h, m, s; + + rb_scan_args(argc, argv, "03", &h, &m, &s); + if (rb_obj_is_kind_of(h, Ctime) == Qtrue) { + TIME_STRUCT *time2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIME_STRUCT, time); + Data_Get_Struct(h, TIME_STRUCT, time2); + *time = *time2; + return self; + } + if (rb_obj_is_kind_of(h, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIME_STRUCT, time); + Data_Get_Struct(h, TIMESTAMP_STRUCT, ts); + time->hour = ts->hour; + time->minute = ts->minute; + time->second = ts->second; + return self; + } + if (rb_obj_is_kind_of(h, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + s = FUNCALL_NOARGS(h, IDsec); + m = FUNCALL_NOARGS(h, IDmin); + h = FUNCALL_NOARGS(h, IDhour); + } else if ((argc == 1) && (rb_obj_is_kind_of(h, rb_cString) == Qtrue)) { + if (time_load1(self, h, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, TIME_STRUCT, time); + time->hour = (h == Qnil) ? 0 : NUM2INT(h); + time->minute = (m == Qnil) ? 0 : NUM2INT(m); + time->second = (s == Qnil) ? 0 : NUM2INT(s); + return self; +} + +static VALUE +time_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + TIME_STRUCT *time1, *time2; + + Data_Get_Struct(self, TIME_STRUCT, time1); + Data_Get_Struct(obj, TIME_STRUCT, time2); + *time2 = *time1; + return obj; +#else + return time_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +time_to_s(VALUE self) +{ + TIME_STRUCT *time; + char buf[128]; + + Data_Get_Struct(self, TIME_STRUCT, time); + sprintf(buf, "%02d:%02d:%02d", time->hour, time->minute, time->second); + return rb_str_new2(buf); +} + +static VALUE +time_dump(VALUE self, VALUE depth) +{ + return time_to_s(self); +} + +static VALUE +time_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +time_hour(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->hour); + } + time->hour = NUM2INT(v); + return self; +} + +static VALUE +time_min(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->minute); + } + time->minute = NUM2INT(v); + return self; +} + +static VALUE +time_sec(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->second); + } + time->second = NUM2INT(v); + return self; +} + +static VALUE +time_cmp(VALUE self, VALUE time) +{ + TIME_STRUCT *time1, *time2; + + if (rb_obj_is_kind_of(time, Ctime) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Time as argument"); + } + Data_Get_Struct(self, TIME_STRUCT, time1); + Data_Get_Struct(time, TIME_STRUCT, time2); + if (time1->hour < time2->hour) { + return INT2FIX(-1); + } + if (time1->hour == time2->hour) { + if (time1->minute < time2->minute) { + return INT2FIX(-1); + } + if (time1->minute == time2->minute) { + if (time1->second < time2->second) { + return INT2FIX(-1); + } + if (time1->second == time2->second) { + return INT2FIX(0); + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * TimeStamp methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +timestamp_alloc(VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + + memset(ts, 0, sizeof (*ts)); + return obj; +} +#else +static VALUE +timestamp_new(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +timestamp_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, !load, !load, &tss)) { + TIMESTAMP_STRUCT *ts; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + } else { + obj = self; + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + } + *ts = tss; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::TimeStamp format error"); + } + return Qnil; +} + +static VALUE +timestamp_load(VALUE self, VALUE str) +{ + return timestamp_load1(self, str, 1); +} + +static VALUE +timestamp_init(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE d, m, y, hh, mm, ss, f; + + rb_scan_args(argc, argv, "07", &y, &m, &d, &hh, &mm, &ss, &f); + if (rb_obj_is_kind_of(y, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(y, TIMESTAMP_STRUCT, ts2); + *ts = *ts2; + return self; + } + if (rb_obj_is_kind_of(y, Cdate) == Qtrue) { + DATE_STRUCT *date; + + if (argc > 1) { + if (argc > 2) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + if (rb_obj_is_kind_of(m, Ctime) == Qtrue) { + TIME_STRUCT *time; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(m, TIME_STRUCT, time); + ts->hour = time->hour; + ts->minute = time->minute; + ts->second = time->second; + } else { + rb_raise(rb_eArgError, "need ODBC::Time argument"); + } + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(y, DATE_STRUCT, date); + ts->year = date->year; + ts->year = date->year; + ts->year = date->year; + ts->fraction = 0; + return self; + } + if (rb_obj_is_kind_of(y, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + f = FUNCALL_NOARGS(y, IDusec); + ss = FUNCALL_NOARGS(y, IDsec); + mm = FUNCALL_NOARGS(y, IDmin); + hh = FUNCALL_NOARGS(y, IDhour); + d = FUNCALL_NOARGS(y, IDday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + f = INT2NUM(NUM2INT(f) * 1000); + } else if (rb_obj_is_kind_of(y, rb_cDate) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + f = INT2FIX(0); + ss = INT2FIX(0); + mm = INT2FIX(0); + hh = INT2FIX(0); + d = FUNCALL_NOARGS(y, IDmday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if ((argc == 1) && (rb_obj_is_kind_of(y, rb_cString) == Qtrue)) { + if (timestamp_load1(self, y, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + ts->year = (y == Qnil) ? 0 : NUM2INT(y); + ts->month = (m == Qnil) ? 0 : NUM2INT(m); + ts->day = (d == Qnil) ? 0 : NUM2INT(d); + ts->hour = (hh == Qnil) ? 0 : NUM2INT(hh); + ts->minute = (mm == Qnil) ? 0 : NUM2INT(mm); + ts->second = (ss == Qnil) ? 0 : NUM2INT(ss); + ts->fraction = (f == Qnil) ? 0 : NUM2INT(f); + return self; +} + +static VALUE +timestamp_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + TIMESTAMP_STRUCT *ts1, *ts2; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts1); + Data_Get_Struct(obj, TIMESTAMP_STRUCT, ts2); + *ts2 = *ts1; + return obj; +#else + return timestamp_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +timestamp_to_s(VALUE self) +{ + TIMESTAMP_STRUCT *ts; + char buf[256]; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d %u", + ts->year, ts->month, ts->day, + ts->hour, ts->minute, ts->second, + (unsigned int) ts->fraction); + return rb_str_new2(buf); +} + +static VALUE +timestamp_dump(VALUE self, VALUE depth) +{ + return timestamp_to_s(self); +} + +static VALUE +timestamp_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +timestamp_year(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->year); + } + ts->year = NUM2INT(v); + return self; +} + +static VALUE +timestamp_month(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->month); + } + ts->month = NUM2INT(v); + return self; +} + +static VALUE +timestamp_day(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->day); + } + ts->day = NUM2INT(v); + return self; +} + +static VALUE +timestamp_hour(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->hour); + } + ts->hour = NUM2INT(v); + return self; +} + +static VALUE +timestamp_min(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->minute); + } + ts->minute = NUM2INT(v); + return self; +} + +static VALUE +timestamp_sec(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->second); + } + ts->second = NUM2INT(v); + return self; +} + +static VALUE +timestamp_fraction(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->fraction); + } + ts->fraction = NUM2INT(v); + return self; +} + +static VALUE +timestamp_cmp(VALUE self, VALUE timestamp) +{ + TIMESTAMP_STRUCT *ts1, *ts2; + + if (rb_obj_is_kind_of(timestamp, Ctimestamp) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::TimeStamp as argument"); + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts1); + Data_Get_Struct(timestamp, TIMESTAMP_STRUCT, ts2); + if (ts1->year < ts2->year) { + return INT2FIX(-1); + } + if (ts1->year == ts2->year) { + if (ts1->month < ts2->month) { + return INT2FIX(-1); + } + if (ts1->month == ts2->month) { + if (ts1->day < ts2->day) { + return INT2FIX(-1); + } + if (ts1->day == ts2->day) { + if (ts1->hour < ts2->hour) { + return INT2FIX(-1); + } + if (ts1->hour == ts2->hour) { + if (ts1->minute < ts2->minute) { + return INT2FIX(-1); + } + if (ts1->minute == ts2->minute) { + if (ts1->second < ts2->second) { + return INT2FIX(-1); + } + if (ts1->second == ts2->second) { + if (ts1->fraction < ts2->fraction) { + return INT2FIX(-1); + } + if (ts1->fraction == ts2->fraction) { + return INT2FIX(0); + } + } + } + } + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * Statement methods. + * + *---------------------------------------------------------------------- + */ + +static VALUE +stmt_drop(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + free_stmt_sub(q, 1); + return self; +} + +static VALUE +stmt_close(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), "SQLFreeStmt(SQL_CLOSE)"); + } + free_stmt_sub(q, 1); + return self; +} + +static VALUE +stmt_cancel(VALUE self) +{ + STMT *q; + char *msg; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLCancel(q->hstmt), &msg, "SQLCancel")) { + rb_raise(Cerror, "%s", msg); + } + } + return self; +} + +static void +check_ncols(STMT *q) +{ + if ((q->hstmt != SQL_NULL_HSTMT) && (q->ncols <= 0) && + (q->coltypes == NULL)) { + COLTYPE *coltypes = NULL; + SQLSMALLINT cols = 0; + + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLNumResultCols(q->hstmt, &cols), NULL, + "SQLNumResultCols") + && (cols > 0)) { + coltypes = make_coltypes(q->hstmt, cols, NULL); + if (coltypes != NULL) { + q->ncols = cols; + q->coltypes = coltypes; + } + } + } +} + +static VALUE +stmt_ncols(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + check_ncols(q); + return INT2FIX(q->ncols); +} + +static VALUE +stmt_nrows(VALUE self) +{ + STMT *q; + SQLLEN rows = -1; + char *msg; + + Data_Get_Struct(self, STMT, q); + if ((q->hstmt != SQL_NULL_HSTMT) && + (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLRowCount(q->hstmt, &rows), &msg, "SQLRowCount"))) { + rb_raise(Cerror, "%s", msg); + } + return INT2NUM(rows); +} + +static VALUE +stmt_nparams(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + return INT2FIX(q->nump); +} + +static int +param_num_check(STMT *q, VALUE pnum, int mkparaminfo, int needout) +{ + int vnum; + + Check_Type(pnum, T_FIXNUM); + vnum = NUM2INT(pnum); + if (mkparaminfo && (q->paraminfo == NULL)) { + char *msg = NULL; + SQLSMALLINT nump = 0; + + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLNumParams(q->hstmt, &nump), NULL, "SQLNumParams")) { + nump = 0; + } + if (nump > 0) { + PARAMINFO *paraminfo = make_paraminfo(q->hstmt, nump, &msg); + + if (paraminfo == NULL) { + rb_raise(Cerror, "%s", msg); + } + q->paraminfo = paraminfo; + if (q->paraminfo != NULL) { + q->nump = nump; + } + } + } + if ((q->paraminfo == NULL) || (vnum < 0) || (vnum >= q->nump)) { + rb_raise(rb_eArgError, "parameter number out of bounds"); + } + if (needout) { + if ((q->paraminfo[vnum].iotype != SQL_PARAM_OUTPUT) && + (q->paraminfo[vnum].iotype != SQL_PARAM_INPUT_OUTPUT)) { + rb_raise(Cerror, "not an output parameter"); + } + } + return vnum; +} + +static VALUE +stmt_param_type(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, ptype, pcoldef, pscale; + int vnum; + STMT *q; + + rb_scan_args(argc, argv, "13", &pnum, &ptype, &pcoldef, &pscale); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 1, 0); + if (argc > 1) { + int vtype, vcoldef, vscale; + + Check_Type(ptype, T_FIXNUM); + vtype = NUM2INT(ptype); + if (argc > 2) { + Check_Type(pcoldef, T_FIXNUM); + vcoldef = NUM2INT(pcoldef); + if (argc > 3) { + Check_Type(pscale, T_FIXNUM); + vscale = NUM2INT(pscale); + q->paraminfo[vnum].scale = vscale; + } + q->paraminfo[vnum].coldef = vcoldef; + } + q->paraminfo[vnum].type = vtype; + q->paraminfo[vnum].override = 1; + return Qnil; + } + return INT2NUM(q->paraminfo[vnum].type); +} + +static VALUE +stmt_param_iotype(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, piotype; + int vnum, viotype; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &piotype); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 1, 0); + if (argc > 1) { + Check_Type(piotype, T_FIXNUM); + viotype = NUM2INT(piotype); + switch (viotype) { + case SQL_PARAM_INPUT: + case SQL_PARAM_INPUT_OUTPUT: + case SQL_PARAM_OUTPUT: + q->paraminfo[vnum].iotype = viotype; + break; + } + } + return INT2NUM(q->paraminfo[vnum].iotype); +} + +static VALUE +stmt_param_output_value(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, v; + int vnum; + STMT *q; + + rb_scan_args(argc, argv, "10", &pnum); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + v = Qnil; + if (q->paraminfo[vnum].rlen == SQL_NULL_DATA) { + return v; + } + if (q->paraminfo[vnum].outbuf == NULL) { + rb_raise(Cerror, "no output value available"); + } + switch (q->paraminfo[vnum].ctype) { + case SQL_C_LONG: + v = INT2NUM(*((SQLINTEGER *) q->paraminfo[vnum].outbuf)); + break; + case SQL_C_DOUBLE: + v = rb_float_new(*((double *) q->paraminfo[vnum].outbuf)); + break; + case SQL_C_DATE: + { + DATE_STRUCT *date; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + const char *p; + char buffer[128]; + VALUE d; + + date = (DATE_STRUCT *) q->paraminfo[vnum].outbuf; + p = (q->dbcp->gmtime == Qtrue) ? "+00:00" : ""; + sprintf(buffer, "%d-%d-%dT00:00:00%s", + date->year, date->month, date->day, p); + d = rb_str_new2(buffer); + v = rb_funcall(rb_cDate, IDparse, 1, d); + } else { + v = Data_Make_Struct(Cdate, DATE_STRUCT, 0, xfree, date); + *date = *((DATE_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; + case SQL_C_TIME: + { + TIME_STRUCT *time; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE now, frac; + + time = (TIME_STRUCT *) q->paraminfo[vnum].outbuf; + frac = rb_float_new(0.0); + now = FUNCALL_NOARGS(rb_cTime, IDnow); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? IDutc : IDlocal, + 7, + FUNCALL_NOARGS(now, IDyear), + FUNCALL_NOARGS(now, IDmonth), + FUNCALL_NOARGS(now, IDday), + INT2NUM(time->hour), + INT2NUM(time->minute), + INT2NUM(time->second), + frac); + } else { + v = Data_Make_Struct(Ctime, TIME_STRUCT, 0, xfree, time); + *time = *((TIME_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; + case SQL_C_TIMESTAMP: + { + TIMESTAMP_STRUCT *ts; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE frac; + + ts = (TIMESTAMP_STRUCT *) q->paraminfo[vnum].outbuf; + frac = rb_float_new((double) 1.0e-3 * ts->fraction); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? IDutc : IDlocal, + 7, + INT2NUM(ts->year), + INT2NUM(ts->month), + INT2NUM(ts->day), + INT2NUM(ts->hour), + INT2NUM(ts->minute), + INT2NUM(ts->second), + frac); + } else { + v = Data_Make_Struct(Ctimestamp, TIMESTAMP_STRUCT, + 0, xfree, ts); + *ts = *((TIMESTAMP_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; +#ifdef UNICODE + case SQL_C_WCHAR: + v = uc_tainted_str_new((SQLWCHAR *) q->paraminfo[vnum].outbuf, + q->paraminfo[vnum].rlen / sizeof (SQLWCHAR)); + break; +#endif + case SQL_C_CHAR: + v = rb_tainted_str_new(q->paraminfo[vnum].outbuf, + q->paraminfo[vnum].rlen); + break; + } + return v; +} + +static VALUE +stmt_param_output_size(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, psize; + int vnum, vsize; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &psize); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + if (argc > 1) { + Check_Type(psize, T_FIXNUM); + vsize = NUM2INT(psize); + if ((vsize > 0) && (vsize < (int) (4 * sizeof (double)))) { + vsize = 4 * sizeof (double); + } + q->paraminfo[vnum].outsize = (vsize > 0) ? vsize : 0; + } + return INT2NUM(q->paraminfo[vnum].outsize); +} + +static VALUE +stmt_param_output_type(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, ptype; + int vnum, vtype; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &ptype); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + if (argc > 1) { + Check_Type(ptype, T_FIXNUM); + vtype = NUM2INT(ptype); + q->paraminfo[vnum].outtype = vtype; + } + return INT2NUM(q->paraminfo[vnum].outtype); +} + +static VALUE +stmt_cursorname(int argc, VALUE *argv, VALUE self) +{ + VALUE cn = Qnil; + STMT *q; +#ifdef UNICODE + SQLWCHAR cname[SQL_MAX_MESSAGE_LENGTH]; + SQLWCHAR *cp; +#else + SQLCHAR cname[SQL_MAX_MESSAGE_LENGTH]; + SQLCHAR *cp; +#endif + char *msg; + SQLSMALLINT cnLen = 0; + + rb_scan_args(argc, argv, "01", &cn); + Data_Get_Struct(self, STMT, q); + if (cn == Qnil) { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLGetCursorName(q->hstmt, (SQLTCHAR *) cname, + (SQLSMALLINT) sizeof (cname), &cnLen), + &msg, "SQLGetCursorName")) { + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + cnLen = (cnLen == 0) ? (SQLSMALLINT) uc_strlen(cname) : + (SQLSMALLINT) (cnLen / sizeof (SQLWCHAR)); + return uc_tainted_str_new(cname, cnLen); +#else + cnLen = (cnLen == 0) ? (SQLSMALLINT) strlen((char *) cname) : cnLen; + return rb_tainted_str_new((char *) cname, cnLen); +#endif + } + if (TYPE(cn) != T_STRING) { + cn = rb_any_to_s(cn); + } +#ifdef UNICODE +#ifdef USE_RB_ENC + cn = rb_funcall(cn, IDencode, 1, rb_encv); +#endif + cp = uc_from_utf((unsigned char *) STR2CSTR(cn), -1); + if (cp == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + cp = (SQLCHAR *) STR2CSTR(cn); +#endif + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLSetCursorName(q->hstmt, cp, SQL_NTS), + &msg, "SQLSetCursorName")) { +#ifdef UNICODE + uc_free(cp); +#endif + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(cp); +#endif + return cn; +} + +static VALUE +stmt_column(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + VALUE col; + int use_scn = 0; + + rb_scan_args(argc, argv, "1", &col); + Check_Type(col, T_FIXNUM); + Data_Get_Struct(self, STMT, q); + check_ncols(q); + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + return make_column(q->hstmt, FIX2INT(col), q->upc, use_scn); +} + +static VALUE +stmt_columns(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + int i, use_scn = 0; + VALUE res, as_ary = Qfalse; + + rb_scan_args(argc, argv, "01", &as_ary); + Data_Get_Struct(self, STMT, q); + check_ncols(q); + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + if (rb_block_given_p()) { + for (i = 0; i < q->ncols; i++) { + rb_yield(make_column(q->hstmt, i, q->upc, use_scn)); + } + return self; + } + if (RTEST(as_ary)) { + res = rb_ary_new2(q->ncols); + } else { + res = rb_hash_new(); + } + for (i = 0; i < q->ncols; i++) { + VALUE obj; + + obj = make_column(q->hstmt, i, q->upc, use_scn); + if (RTEST(as_ary)) { + rb_ary_store(res, i, obj); + } else { + VALUE name = rb_iv_get(obj, "@name"); + + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char buf[32]; + + sprintf(buf, "#%d", i); + name = rb_str_dup(name); + name = rb_obj_taint(rb_str_cat2(name, buf)); + } + rb_hash_aset(res, name, obj); + } + } + return res; +} + +static VALUE +stmt_param(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + VALUE par; + int i; + + rb_scan_args(argc, argv, "1", &par); + Check_Type(par, T_FIXNUM); + Data_Get_Struct(self, STMT, q); + i = FIX2INT(par); + if ((i < 0) || (i >= q->nump)) { + rb_raise(Cerror, "%s", set_err("Parameter out of bounds", 0)); + } + return make_param(q, i); +} + +static VALUE +stmt_params(VALUE self) +{ + STMT *q; + int i; + VALUE res; + + Data_Get_Struct(self, STMT, q); + if (rb_block_given_p()) { + for (i = 0; i < q->nump; i++) { + rb_yield(make_param(q, i)); + } + return self; + } + res = rb_ary_new2(q->nump); + for (i = 0; i < q->nump; i++) { + VALUE obj; + + obj = make_param(q, i); + rb_ary_store(res, i, obj); + } + return res; +} + +static VALUE +do_fetch(STMT *q, int mode) +{ + int i, use_scn = 0, offc; + char **bufs, *msg; + VALUE res; + + if (q->ncols <= 0) { + rb_raise(Cerror, "%s", set_err("No columns in result set", 0)); + } + if (++q->fetchc >= 500) { + q->fetchc = 0; + start_gc(); + } + bufs = q->dbufs; + if (bufs == NULL) { + int need = sizeof (char *) * q->ncols, needp; + char *p; + + need = LEN_ALIGN(need); + needp = need; + for (i = 0; i < q->ncols; i++) { + if (q->coltypes[i].size != SQL_NO_TOTAL) { + need += LEN_ALIGN(q->coltypes[i].size); + } + } + p = ALLOC_N(char, need); + if (p == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + q->dbufs = bufs = (char **) p; + p += needp; + for (i = 0; i < q->ncols; i++) { + int len = q->coltypes[i].size; + + if (len == SQL_NO_TOTAL) { + bufs[i] = NULL; + } else { + bufs[i] = p; + p += LEN_ALIGN(len); + } + } + } + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASH: + case DOFETCH_HASH2: + case DOFETCH_HASHK: + case DOFETCH_HASHK2: + if (q->colnames == NULL) { + int need = sizeof (char *) * 4 * q->ncols + sizeof (char *); + int max_len[2] = { 0, 0 }; + char **na, *p; +#ifdef UNICODE + SQLWCHAR name[SQL_MAX_MESSAGE_LENGTH]; +#else + char name[SQL_MAX_MESSAGE_LENGTH]; +#endif + SQLSMALLINT name_len; + + for (i = 0; i < q->ncols; i++) { + int need_len; + + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, + (SQLUSMALLINT) (i + 1), + SQL_COLUMN_TABLE_NAME, + name, + sizeof (name), + &name_len, NULL), + &msg, + "SQLColAttributes(SQL_COLUMN_TABLE_NAME)")) { + rb_raise(Cerror, "%s", msg); + } + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + need_len = 6 * (uc_strlen(name) + 1); +#else + need_len = 2 * (strlen(name) + 1); +#endif + need += need_len; + if (max_len[0] < need_len) { + max_len[0] = need_len; + } + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, + (SQLUSMALLINT) (i + 1), + use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + sizeof (name), + &name_len, NULL), + &msg, use_scn ? + "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)")) { + rb_raise(Cerror, "%s", msg); + } + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + need_len = 6 * 2 * (uc_strlen(name) + 1); +#else + need_len = 2 * (strlen(name) + 1); +#endif + need += need_len; + if (max_len[1] < need_len) { + max_len[1] = need_len; + } + } + need += max_len[0] + max_len[1] + 32; + p = ALLOC_N(char, need); + if (p == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + na = (char **) p; + p += sizeof (char *) * 4 * q->ncols + sizeof (char *); + for (i = 0; i < q->ncols; i++) { + char *p0; + + name[0] = 0; + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, (SQLUSMALLINT) (i + 1), + SQL_COLUMN_TABLE_NAME, name, + sizeof (name), &name_len, NULL), + "SQLColAttributes(SQL_COLUMN_TABLE_NAME)"); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } + na[i + q->ncols] = p; +#ifdef UNICODE + p += mkutf(p, name, uc_strlen(name)); +#else + strcpy(p, name); +#endif + strcat(p, "."); + p += strlen(p); + p0 = p; + name[0] = 0; + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, (SQLUSMALLINT) (i + 1), + use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + sizeof (name), &name_len, NULL), + use_scn ? "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)"); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } + na[i] = p; +#ifdef UNICODE + p += mkutf(p, name, uc_strlen(name)) + 1; +#else + strcpy(p, name); + p += strlen(p) + 1; +#endif + na[i + 3 * q->ncols] = p; + strcpy(p, na[i + q->ncols]); + p += p0 - na[i + q->ncols]; + na[i + 2 * q->ncols] = upcase_if(p, 1); + p += strlen(p) + 1; + } + /* reserved space for later adjustments */ + na[4 * q->ncols] = p; + q->colnames = na; + if (q->colvals == NULL) { + q->colvals = ALLOC_N(VALUE, 4 * q->ncols); + if (q->colvals != NULL) { + VALUE cname; + VALUE colbuf[4]; + + for (i = 0; i < 4 * q->ncols; i++) { + q->colvals[i] = Qnil; + } + for (i = 0; i < 4; i++) { + colbuf[i] = rb_iv_get(q->self, colnamebuf[i]); + if (colbuf[i] == Qnil) { + res = rb_hash_new(); + rb_iv_set(q->self, colnamebuf[i], res); + } + } + for (i = 0; i < 4 * q->ncols; i++) { + res = colbuf[i / q->ncols]; + cname = rb_tainted_str_new2(q->colnames[i]); +#ifdef USE_RB_ENC + rb_enc_associate(cname, rb_enc); +#endif + q->colvals[i] = cname; + if (rb_funcall(res, IDkeyp, 1, cname) == Qtrue) { + char *p; + + cname = rb_tainted_str_new2(q->colnames[i]); +#ifdef USE_RB_ENC + rb_enc_associate(cname, rb_enc); +#endif + p = q->colnames[4 * q->ncols]; + sprintf(p, "#%d", i); + cname = rb_str_cat2(cname, p); + q->colvals[i] = cname; + } + rb_obj_freeze(cname); + rb_hash_aset(res, cname, Qtrue); + } + } + } + } + /* FALL THRU */ + case DOFETCH_HASHN: + if (mode & DOFETCH_BANG) { + res = rb_iv_get(q->self, "@_h"); + if (res == Qnil) { + res = rb_hash_new(); + rb_iv_set(q->self, "@_h", res); + } + } else { + res = rb_hash_new(); + } + break; + default: + if (mode & DOFETCH_BANG) { + res = rb_iv_get(q->self, "@_a"); + if (res == Qnil) { + res = rb_ary_new2(q->ncols); + rb_iv_set(q->self, "@_a", res); + } else { + rb_ary_clear(res); + } + } else { + res = rb_ary_new2(q->ncols); + } + } + offc = q->upc ? (2 * q->ncols) : 0; + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASHK2: + case DOFETCH_HASH2: + offc += q->ncols; + break; + } + for (i = 0; i < q->ncols; i++) { + SQLLEN totlen; + SQLLEN curlen = q->coltypes[i].size; + SQLSMALLINT type = q->coltypes[i].type; + VALUE v, name; + char *valp, *freep = NULL; + + if (curlen == SQL_NO_TOTAL) { + SQLLEN chunksize = SEGSIZE; + + totlen = 0; +#ifdef UNICODE + valp = ALLOC_N(char, chunksize + sizeof (SQLWCHAR)); +#else + valp = ALLOC_N(char, chunksize + 1); +#endif + freep = valp; + while ((curlen == SQL_NO_TOTAL) || (curlen > chunksize)) { + SQLRETURN rc; + int ret; + + rc = SQLGetData(q->hstmt, (SQLUSMALLINT) (i + 1), + type, (SQLPOINTER) (valp + totlen), +#ifdef UNICODE + ((type == SQL_C_CHAR) || (type == SQL_C_WCHAR)) ? + (chunksize + (int) sizeof (SQLWCHAR)) : + chunksize, +#else + (type == SQL_C_CHAR) ? + (chunksize + 1) : chunksize, +#endif + &curlen); + if (rc == SQL_NO_DATA) { + if (curlen == SQL_NO_TOTAL) { + curlen = totlen; + } + break; + } + ret = succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + rc, &msg, "SQLGetData"); + if (!ret) { + xfree(valp); + rb_raise(Cerror, "%s", msg); + } + if (curlen == SQL_NULL_DATA) { + break; + } + if (curlen == SQL_NO_TOTAL) { + totlen += chunksize; + } else if (curlen > chunksize) { + totlen += chunksize; + chunksize = curlen - chunksize; + } else { + totlen += curlen; + break; + } +#ifdef UNICODE + REALLOC_N(valp, char, totlen + chunksize + sizeof (SQLWCHAR)); +#else + REALLOC_N(valp, char, totlen + chunksize + 1); +#endif + if (valp == NULL) { + if (freep != NULL) { + xfree(freep); + } + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + freep = valp; + } + if (totlen > 0) { + curlen = totlen; + } + } else { + totlen = curlen; + valp = bufs[i]; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLGetData(q->hstmt, (SQLUSMALLINT) (i + 1), type, + (SQLPOINTER) valp, totlen, &curlen), + &msg, "SQLGetData")) { + rb_raise(Cerror, "%s", msg); + } + } + if (curlen == SQL_NULL_DATA) { + v = Qnil; + } else { + switch (type) { + case SQL_C_LONG: + v = INT2NUM(*((SQLINTEGER *) valp)); + break; + case SQL_C_DOUBLE: + v = rb_float_new(*((double *) valp)); + break; +#ifdef SQL_C_SBIGINT + case SQL_C_SBIGINT: +#ifdef LL2NUM + v = LL2NUM(*((SQLBIGINT *) valp)); +#else + v = INT2NUM(*((SQLBIGINT *) valp)); +#endif + break; +#endif +#ifdef SQL_C_UBIGINT + case SQL_C_UBIGINT: +#ifdef LL2NUM + v = ULL2NUM(*((SQLBIGINT *) valp)); +#else + v = UINT2NUM(*((SQLBIGINT *) valp)); +#endif + break; +#endif + case SQL_C_DATE: + { + DATE_STRUCT *date; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + const char *p; + char buffer[128]; + VALUE d; + + date = (DATE_STRUCT *) valp; + p = (q->dbcp->gmtime == Qtrue) ? "+00:00" : ""; + sprintf(buffer, "%d-%d-%dT00:00:00%s", + date->year, date->month, date->day, p); + d = rb_str_new2(buffer); + v = rb_funcall(rb_cDate, IDparse, 1, d); + } else { + v = Data_Make_Struct(Cdate, DATE_STRUCT, 0, xfree, + date); + *date = *(DATE_STRUCT *) valp; + } + } + break; + case SQL_C_TIME: + { + TIME_STRUCT *time; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE now, frac; + + time = (TIME_STRUCT *) valp; + frac = rb_float_new(0.0); + now = FUNCALL_NOARGS(rb_cTime, IDnow); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? + IDutc : IDlocal, + 7, + FUNCALL_NOARGS(now, IDyear), + FUNCALL_NOARGS(now, IDmonth), + FUNCALL_NOARGS(now, IDday), + INT2NUM(time->hour), + INT2NUM(time->minute), + INT2NUM(time->second), + frac); + } else { + v = Data_Make_Struct(Ctime, TIME_STRUCT, 0, xfree, + time); + *time = *(TIME_STRUCT *) valp; + } + } + break; + case SQL_C_TIMESTAMP: + { + TIMESTAMP_STRUCT *ts; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE frac; + + ts = (TIMESTAMP_STRUCT *) valp; + frac = rb_float_new((double) 1.0e-3 * ts->fraction); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? + IDutc : IDlocal, + 7, + INT2NUM(ts->year), + INT2NUM(ts->month), + INT2NUM(ts->day), + INT2NUM(ts->hour), + INT2NUM(ts->minute), + INT2NUM(ts->second), + frac); + } else { + v = Data_Make_Struct(Ctimestamp, TIMESTAMP_STRUCT, + 0, xfree, ts); + *ts = *(TIMESTAMP_STRUCT *) valp; + } + } + break; +#ifdef UNICODE + case SQL_C_WCHAR: + v = uc_tainted_str_new((SQLWCHAR *) valp, + curlen / sizeof (SQLWCHAR)); + break; +#endif + default: + v = rb_tainted_str_new(valp, curlen); + break; + } + } + if (freep != NULL) { + xfree(freep); + } + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASH: + case DOFETCH_HASH2: + valp = q->colnames[i + offc]; + name = (q->colvals == NULL) ? Qnil : q->colvals[i + offc]; + if (name == Qnil) { + name = rb_tainted_str_new2(valp); +#ifdef USE_RB_ENC + rb_enc_associate(name, rb_enc); +#endif + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char *p; + + name = rb_tainted_str_new2(valp); +#ifdef USE_RB_ENC + rb_enc_associate(name, rb_enc); +#endif + p = q->colnames[4 * q->ncols]; + sprintf(p, "#%d", i); + name = rb_str_cat2(name, p); + } + } + rb_hash_aset(res, name, v); + break; + case DOFETCH_HASHK: + case DOFETCH_HASHK2: + valp = q->colnames[i + offc]; +#ifdef USE_RB_ENC + name = ID2SYM(rb_intern3(valp, strlen(valp), rb_enc)); +#else + name = ID2SYM(rb_intern(valp)); +#endif + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char *p; + + p = q->colnames[4 * q->ncols]; + sprintf(p, "%s#%d", valp, i); +#ifdef USE_RB_ENC + name = ID2SYM(rb_intern3(p, strlen(p), rb_enc)); +#else + name = ID2SYM(rb_intern(p)); +#endif + } + rb_hash_aset(res, name, v); + break; + case DOFETCH_HASHN: + name = INT2NUM(i); + rb_hash_aset(res, name, v); + break; + default: + rb_ary_push(res, v); + } + } + return res; +} + +static VALUE +stmt_fetch1(VALUE self, int bang) +{ + STMT *q; + SQLRETURN ret; + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (q->usef) { + goto usef; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_NEXT)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_NEXT, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_NEXT)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_NEXT, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + if ((err != NULL) && + ((strncmp(err, "IM001", 5) == 0) || + (strncmp(err, "HYC00", 5) == 0))) { +usef: + /* Fallback to SQLFetch() when others not implemented */ + msg = "SQLFetch"; + q->usef = 1; + ret = SQLFETCH(q->hstmt); + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch(VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each(self); + } + return stmt_fetch1(self, 0); +} + +static VALUE +stmt_fetch_bang(VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each(self); + } + return stmt_fetch1(self, 1); +} + +static VALUE +stmt_fetch_first1(VALUE self, int bang, int nopos) +{ + STMT *q; + SQLRETURN ret; + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (nopos) { + goto dofetch; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_FIRST)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_FIRST)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { +dofetch: + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_first(VALUE self) +{ + return stmt_fetch_first1(self, 0, 0); +} + +static VALUE +stmt_fetch_first_bang(VALUE self) +{ + return stmt_fetch_first1(self, 1, 0); +} + +static VALUE +stmt_fetch_scroll1(int argc, VALUE *argv, VALUE self, int bang) +{ + STMT *q; + VALUE dir, offs; + SQLRETURN ret; + int idir, ioffs = 1; + char msg[128], *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + rb_scan_args(argc, argv, "11", &dir, &offs); + idir = NUM2INT(dir); + if (offs != Qnil) { + ioffs = NUM2INT(offs); + } + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } +#if (ODBCVER < 0x0300) + sprintf(msg, "SQLExtendedFetch(%d)", idir); + ret = SQLEXTENDEDFETCH(q->hstmt, (SQLSMALLINT) idir, (SQLROWOFFSET) ioffs, + &nRows, rowStat); +#else + sprintf(msg, "SQLFetchScroll(%d)", idir); + ret = SQLFETCHSCROLL(q->hstmt, (SQLSMALLINT) idir, (SQLROWOFFSET) ioffs); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_scroll(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_scroll1(argc, argv, self, 0); +} + +static VALUE +stmt_fetch_scroll_bang(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_scroll1(argc, argv, self, 1); +} + +static VALUE +stmt_fetch_many(VALUE self, VALUE arg) +{ + int i, max = 0, all = arg == Qnil; + VALUE res; + + if (!all) { + max = NUM2INT(arg); + } + res = rb_ary_new(); + for (i = 0; all || (i < max); i++) { + VALUE v = stmt_fetch1(self, 0); + + if (v == Qnil) { + break; + } + rb_ary_push(res, v); + } + return (i == 0) ? Qnil : res; +} + +static VALUE +stmt_fetch_all(VALUE self) +{ + return stmt_fetch_many(self, Qnil); +} + +static int +stmt_hash_mode(int argc, VALUE *argv, VALUE self) +{ + VALUE withtab = Qnil, usesym = Qnil; + int mode = DOFETCH_HASH; + + rb_scan_args(argc, argv, "02", &withtab, &usesym); + if ((withtab != Qtrue) && (withtab != Qfalse) && (withtab != Modbc) && + (rb_obj_is_kind_of(withtab, rb_cHash) == Qtrue)) { + VALUE v; + + v = rb_hash_aref(withtab, ID2SYM(IDkey)); + if (v == ID2SYM(IDSymbol)) { + mode = DOFETCH_HASHK; + } else if (v == ID2SYM(IDString)) { + mode = DOFETCH_HASH; + } else if (v == ID2SYM(IDFixnum)) { + mode = DOFETCH_HASHN; + } else { + rb_raise(Cerror, "Unsupported key mode"); + } + if (mode != DOFETCH_HASHN) { + v = rb_hash_aref(withtab, ID2SYM(IDtable_names)); + if (RTEST(v)) { + mode = (mode == DOFETCH_HASHK) + ? DOFETCH_HASHK2 : DOFETCH_HASH2; + } + } + return mode; + } + if (withtab == Modbc) { + return DOFETCH_HASHN; + } + mode = RTEST(withtab) ? DOFETCH_HASH2 : DOFETCH_HASH; + if (RTEST(usesym)) { + mode = (mode == DOFETCH_HASH2) ? DOFETCH_HASHK2 : DOFETCH_HASHK; + } + return mode; +} + +static VALUE +stmt_fetch_hash1(int argc, VALUE *argv, VALUE self, int bang) +{ + STMT *q; + SQLRETURN ret; + int mode = stmt_hash_mode(argc, argv, self); + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (q->usef) { + goto usef; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_NEXT)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_NEXT, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_NEXT)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_NEXT, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + if ((err != NULL) && + ((strncmp(err, "IM001", 5) == 0) || + (strncmp(err, "HYC00", 5) == 0))) { +usef: + /* Fallback to SQLFetch() when others not implemented */ + msg = "SQLFetch"; + q->usef = 1; + ret = SQLFETCH(q->hstmt); + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &err, msg)) { + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_hash(int argc, VALUE *argv, VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each_hash(argc, argv, self); + } + return stmt_fetch_hash1(argc, argv, self, 0); +} + +static VALUE +stmt_fetch_hash_bang(int argc, VALUE *argv, VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each_hash(argc, argv, self); + } + return stmt_fetch_hash1(argc, argv, self, 1); +} + +static VALUE +stmt_fetch_first_hash1(int argc, VALUE *argv, VALUE self, int bang, int nopos) +{ + STMT *q; + SQLRETURN ret; + int mode = stmt_hash_mode(argc, argv, self); + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (nopos) { + goto dofetch; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_FIRST)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_FIRST)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { +dofetch: + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_first_hash(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_first_hash1(argc, argv, self, 0, 0); +} + +static VALUE +stmt_each(VALUE self) +{ + VALUE row, res = Qnil; + STMT *q; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); +#if (ODBCVER < 0x0300) + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, + rowStat), + "SQLExtendedFetch(SQL_FETCH_FIRST)")) +#else + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0), + "SQLFetchScroll(SQL_FETCH_FIRST)")) +#endif + { + case SQL_NO_DATA: + row = Qnil; + break; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + row = stmt_fetch_first1(self, 0, 1); + break; + default: + row = stmt_fetch1(self, 0); + } + if (rb_block_given_p()) { + while (row != Qnil) { + rb_yield(row); + row = stmt_fetch1(self, 0); + } + return self; + } + if (row != Qnil) { + res = rb_ary_new(); + while (row != Qnil) { + rb_ary_push(res, row); + row = stmt_fetch1(self, 0); + } + } + return res; +} + +static VALUE +stmt_each_hash(int argc, VALUE *argv, VALUE self) +{ + VALUE row, res = Qnil, withtab[2]; + STMT *q; + int mode = stmt_hash_mode(argc, argv, self); +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + if (mode == DOFETCH_HASHN) { + withtab[0] = Modbc; + withtab[1] = Qfalse; + } else { + withtab[0] = ((mode == DOFETCH_HASH2) || (mode == DOFETCH_HASHK2)) + ? Qtrue : Qfalse; + withtab[1] = ((mode == DOFETCH_HASHK) || (mode == DOFETCH_HASHK2)) + ? Qtrue : Qfalse; + } + Data_Get_Struct(self, STMT, q); +#if (ODBCVER < 0x0300) + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, + rowStat), + "SQLExtendedFetch(SQL_FETCH_FIRST)")) +#else + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0), + "SQLFetchScroll(SQL_FETCH_FIRST)")) +#endif + { + case SQL_NO_DATA: + row = Qnil; + break; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + row = stmt_fetch_first_hash1(2, withtab, self, 0, 1); + break; + default: + row = stmt_fetch_hash1(2, withtab, self, 0); + } + if (rb_block_given_p()) { + while (row != Qnil) { + rb_yield(row); + row = stmt_fetch_hash1(2, withtab, self, 0); + } + return self; + } + if (row != Qnil) { + res = rb_ary_new(); + while (row != Qnil) { + rb_ary_push(res, row); + row = stmt_fetch_hash1(2, withtab, self, 0); + } + } + return res; +} + +static VALUE +stmt_more_results(VALUE self) +{ + STMT *q; + + if (rb_block_given_p()) { + rb_raise(rb_eArgError, "block not allowed"); + } + Data_Get_Struct(self, STMT, q); + if (q->hstmt == SQL_NULL_HSTMT) { + return Qfalse; + } + switch (tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLMoreResults(q->hstmt), "SQLMoreResults")) { + case SQL_NO_DATA: + return Qfalse; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + free_stmt_sub(q, 0); + make_result(q->dbc, q->hstmt, self, 0); + break; + default: + rb_raise(Cerror, "%s", + get_err(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt)); + } + return Qtrue; +} + +static VALUE +stmt_prep_int(int argc, VALUE *argv, VALUE self, int mode) +{ + DBC *p = get_dbc(self); + STMT *q = NULL; + VALUE sql, dbc, stmt; + SQLHSTMT hstmt; + SQLRETURN ret; +#ifdef UNICODE + SQLWCHAR *ssql = NULL; +#else + SQLCHAR *ssql = NULL; +#endif + char *csql = NULL, *msg = NULL; + + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + Data_Get_Struct(self, STMT, q); + free_stmt_sub(q, 0); + if (q->hstmt == SQL_NULL_HSTMT) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, q->hstmt, + SQLAllocStmt(p->hdbc, &q->hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + } else if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), + &msg, "SQLFreeStmt(SQL_CLOSE)")) { + rb_raise(Cerror, "%s", msg); + } + hstmt = q->hstmt; + stmt = self; + dbc = q->dbc; + } else { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + stmt = Qnil; + dbc = self; + } + rb_scan_args(argc, argv, "1", &sql); + Check_Type(sql, T_STRING); +#ifdef UNICODE +#ifdef USE_RB_ENC + sql = rb_funcall(sql, IDencode, 1, rb_encv); +#endif + csql = STR2CSTR(sql); + ssql = uc_from_utf((unsigned char *) csql, -1); + if (ssql == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + csql = STR2CSTR(sql); + ssql = (SQLCHAR *) csql; +#endif + if ((mode & MAKERES_EXECD)) { + ret = SQLEXECDIRECT(hstmt, ssql, SQL_NTS); + if (!succeeded_nodata(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, ret, + &msg, "SQLExecDirect('%s')", csql)) { + goto sqlerr; + } + if (ret == SQL_NO_DATA) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_CLOSE), "SQLFreeStmt(SQL_DROP)"); + if (q != NULL) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + hstmt = SQL_NULL_HSTMT; + } + } else { + ret = SQLPREPARE(hstmt, ssql, SQL_NTS); + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, ret, + &msg, "SQLPrepare('%s')", csql)) { +sqlerr: +#ifdef UNICODE + uc_free(ssql); +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + if (q != NULL) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + rb_raise(Cerror, "%s", msg); + } else { + mode |= MAKERES_PREPARE; + } + } +#ifdef UNICODE + uc_free(ssql); +#endif + return make_result(dbc, hstmt, stmt, mode); +} + +static VALUE +stmt_prep(int argc, VALUE *argv, VALUE self) +{ + return stmt_prep_int(argc, argv, self, MAKERES_BLOCK); +} + +static int +bind_one_param(int pnum, VALUE arg, STMT *q, char **msgp, int *outpp) +{ + SQLPOINTER valp = (SQLPOINTER) &q->paraminfo[pnum].buffer; + SQLSMALLINT ctype, stype; + SQLINTEGER vlen, rlen; + SQLUINTEGER coldef; +#ifdef NO_RB_STR2CSTR + VALUE val; +#endif + long llen; + int retry = 1; +#ifdef UNICODE + SQLWCHAR *up; + + q->paraminfo[pnum].tofree = NULL; +#endif + switch (TYPE(arg)) { + case T_STRING: +#ifdef UNICODE + ctype = SQL_C_WCHAR; +#ifdef USE_RB_ENC + arg = rb_funcall(arg, IDencode, 1, rb_encv); +#endif +#ifndef NO_RB_STR2CSTR + up = (SQLWCHAR *) rb_str2cstr(arg, &llen); + if (llen != (long) strlen((char *) up)) { + ctype = SQL_C_BINARY; + valp = (SQLPOINTER) up; + rlen = llen; + vlen = rlen + 1; + break; + } +#else + val = rb_string_value(&arg); + up = (SQLWCHAR *) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (up == NULL) { + goto oom; + } + if (memchr((char *) up, 0, llen)) { + ctype = SQL_C_BINARY; + valp = (SQLPOINTER) up; + rlen = llen; + vlen = rlen + 1; + break; + } + up = (SQLWCHAR *) rb_string_value_cstr(&arg); +#endif + up = uc_from_utf((unsigned char *) up, llen); + if (up == NULL) { + goto oom; + } + *(SQLWCHAR **) valp = up; + rlen = uc_strlen(up) * sizeof (SQLWCHAR); + vlen = rlen + sizeof (SQLWCHAR); + q->paraminfo[pnum].tofree = up; +#else + ctype = SQL_C_CHAR; +#ifndef NO_RB_STR2CSTR + valp = (SQLPOINTER) rb_str2cstr(arg, &llen); + rlen = llen; + if (rlen != (SQLINTEGER) strlen((char *) valp)) { + ctype = SQL_C_BINARY; + } + vlen = rlen + 1; +#else + val = rb_string_value(&arg); + valp = (SQLPOINTER) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (valp == NULL) { + goto oom; + } + rlen = llen; + vlen = rlen + 1; + if (memchr((char *) valp, 0, llen)) { + ctype = SQL_C_BINARY; + break; + } + valp = (SQLPOINTER) rb_string_value_cstr(&arg); +#endif +#endif + break; + case T_FIXNUM: + ctype = SQL_C_LONG; + *(SQLINTEGER *) valp = FIX2INT(arg); + rlen = 1; + vlen = sizeof (SQLINTEGER); + break; + case T_FLOAT: + ctype = SQL_C_DOUBLE; + *(double *) valp = NUM2DBL(arg); + rlen = 1; + vlen = sizeof (double); + break; + case T_NIL: + ctype = SQL_C_CHAR; + valp = NULL; + rlen = SQL_NULL_DATA; + vlen = 0; + break; + case T_SYMBOL: + ctype = SQL_C_CHAR; + valp = NULL; + vlen = 0; + if (arg == ID2SYM(IDNULL)) { + rlen = SQL_NULL_DATA; + } else if (arg == ID2SYM(IDdefault)) { + rlen = SQL_DEFAULT_PARAM; + } + /* fall through */ + default: + if (rb_obj_is_kind_of(arg, Cdate) == Qtrue) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + Data_Get_Struct(arg, DATE_STRUCT, date); + valp = (SQLPOINTER) date; + rlen = 1; + vlen = sizeof (DATE_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, Ctime) == Qtrue) { + TIME_STRUCT *time; + + ctype = SQL_C_TIME; + Data_Get_Struct(arg, TIME_STRUCT, time); + valp = (SQLPOINTER) time; + rlen = 1; + vlen = sizeof (TIME_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + ctype = SQL_C_TIMESTAMP; + Data_Get_Struct(arg, TIMESTAMP_STRUCT, ts); + valp = (SQLPOINTER) ts; + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, rb_cTime) == Qtrue) { + if (q->paraminfo[pnum].type == SQL_TIME) { + TIME_STRUCT *time; + + ctype = SQL_C_TIME; + time = (TIME_STRUCT *) valp; + memset(time, 0, sizeof (TIME_STRUCT)); + time->hour = FUNCALL_NOARGS(arg, IDhour); + time->minute = FUNCALL_NOARGS(arg, IDmin); + time->second = FUNCALL_NOARGS(arg, IDsec); + rlen = 1; + vlen = sizeof (TIME_STRUCT); + } else if (q->paraminfo[pnum].type == SQL_DATE) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + date = (DATE_STRUCT *) valp; + memset(date, 0, sizeof (DATE_STRUCT)); + date->year = FUNCALL_NOARGS(arg, IDyear); + date->month = FUNCALL_NOARGS(arg, IDmonth); + date->day = FUNCALL_NOARGS(arg, IDday); + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + } else { + TIMESTAMP_STRUCT *ts; + + ctype = SQL_C_TIMESTAMP; + ts = (TIMESTAMP_STRUCT *) valp; + memset(ts, 0, sizeof (TIMESTAMP_STRUCT)); + ts->year = FUNCALL_NOARGS(arg, IDyear); + ts->month = FUNCALL_NOARGS(arg, IDmonth); + ts->day = FUNCALL_NOARGS(arg, IDday); + ts->hour = FUNCALL_NOARGS(arg, IDhour); + ts->minute = FUNCALL_NOARGS(arg, IDmin); + ts->second = FUNCALL_NOARGS(arg, IDsec); +#ifdef TIME_USE_USEC + ts->fraction = FUNCALL_NOARGS(arg, IDusec) * 1000; +#else + ts->fraction = FUNCALL_NOARGS(arg, IDnsec); +#endif + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + } + break; + } + if (rb_obj_is_kind_of(arg, rb_cDate) == Qtrue) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + date = (DATE_STRUCT *) valp; + memset(date, 0, sizeof (DATE_STRUCT)); + date->year = FUNCALL_NOARGS(arg, IDyear); + date->month = FUNCALL_NOARGS(arg, IDmonth); + date->day = FUNCALL_NOARGS(arg, IDmday); + rlen = 1; + vlen = sizeof (DATE_STRUCT); + break; + } + ctype = SQL_C_CHAR; +#ifndef NO_RB_STR2CSTR + valp = (SQLPOINTER *) rb_str2cstr(rb_str_to_str(arg), &llen); + rlen = llen; + if (rlen != (SQLINTEGER) strlen((char *) valp)) { + ctype = SQL_C_BINARY; + } + vlen = rlen + 1; +#else + val = rb_string_value(&arg); + valp = (SQLPOINTER) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (valp == NULL) { + goto oom; + } + rlen = llen; + vlen = rlen + 1; + if (memchr((char *) valp, 0, llen)) { + ctype = SQL_C_BINARY; + break; + } + valp = (SQLPOINTER) rb_string_value_cstr(&arg); +#endif + break; + } + stype = q->paraminfo[pnum].type; + coldef = q->paraminfo[pnum].coldef; + q->paraminfo[pnum].rlen = rlen; + q->paraminfo[pnum].ctype = ctype; + if (coldef == 0) { + switch (ctype) { + case SQL_C_LONG: + coldef = 10; + break; + case SQL_C_DOUBLE: + coldef = 15; + if (stype == SQL_VARCHAR) { + stype = SQL_DOUBLE; + } + break; + case SQL_C_DATE: + coldef = 10; + break; + case SQL_C_TIME: + coldef = 8; + break; + case SQL_C_TIMESTAMP: + coldef = 19; + break; + default: + /* + * Patch adopted from the Perl DBD::ODBC module ... + * per patch from Paul G. Weiss, who was experiencing re-preparing + * of queries when the size of the bound string's were increasing + * for example select * from tabtest where name = ? + * then executing with 'paul' and then 'thomas' would cause + * SQLServer to prepare the query twice, but if we ran 'thomas' + * then 'paul', it would not re-prepare the query. The key seems + * to be allocating enough space for the largest parameter. + * TBD: the default for this should be a tunable parameter. + */ + if ((stype == SQL_VARCHAR) && + (q->paraminfo[pnum].iotype != SQL_PARAM_INPUT_OUTPUT) && + (q->paraminfo[pnum].iotype != SQL_PARAM_OUTPUT)) { + if (q->paraminfo[pnum].coldef_max == 0) { + q->paraminfo[pnum].coldef_max = (vlen > 128) ? vlen : 128; + } else { + /* bump up max, if needed */ + if (vlen > (SQLINTEGER) q->paraminfo[pnum].coldef_max) { + q->paraminfo[pnum].coldef_max = vlen; + } + } + coldef = q->paraminfo[pnum].coldef_max; + } else { + coldef = vlen; + } + break; + } + } + if ((q->paraminfo[pnum].iotype == SQL_PARAM_INPUT_OUTPUT) || + (q->paraminfo[pnum].iotype == SQL_PARAM_OUTPUT)) { + if (valp == NULL) { + if (q->paraminfo[pnum].outsize > 0) { + if (q->paraminfo[pnum].outbuf != NULL) { + xfree(q->paraminfo[pnum].outbuf); + } + q->paraminfo[pnum].outbuf = xmalloc(q->paraminfo[pnum].outsize); + if (q->paraminfo[pnum].outbuf == NULL) { + goto oom; + } + ctype = q->paraminfo[pnum].ctype = q->paraminfo[pnum].outtype; + outpp[0]++; + valp = q->paraminfo[pnum].outbuf; + vlen = q->paraminfo[pnum].outsize; + } + } else { + if (q->paraminfo[pnum].outbuf != NULL) { + xfree(q->paraminfo[pnum].outbuf); + } + q->paraminfo[pnum].outbuf = xmalloc(vlen); + if (q->paraminfo[pnum].outbuf == NULL) { +oom: +#ifdef UNICODE + if (q->paraminfo[pnum].tofree != NULL) { + uc_free(q->paraminfo[pnum].tofree); + q->paraminfo[pnum].tofree = NULL; + } +#endif + *msgp = set_err("Out of memory", 0); + return -1; + } +#ifdef UNICODE + if (ctype == SQL_C_WCHAR) { + memcpy(q->paraminfo[pnum].outbuf, *(SQLWCHAR **) valp, vlen); + } else +#endif + memcpy(q->paraminfo[pnum].outbuf, valp, vlen); +#ifdef UNICODE + if (ctype == SQL_C_WCHAR) { + *(SQLWCHAR **) valp = (SQLWCHAR *) q->paraminfo[pnum].outbuf; + } else +#endif + valp = q->paraminfo[pnum].outbuf; + outpp[0]++; + } + } +retry: +#ifdef UNICODE + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLBindParameter(q->hstmt, (SQLUSMALLINT) (pnum + 1), + q->paraminfo[pnum].iotype, + ctype, stype, coldef, + q->paraminfo[pnum].scale, + (ctype == SQL_C_WCHAR) ? + *(SQLWCHAR **) valp : valp, + vlen, &q->paraminfo[pnum].rlen), + msgp, "SQLBindParameter(%d)", pnum + 1)) +#else + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLBindParameter(q->hstmt, (SQLUSMALLINT) (pnum + 1), + q->paraminfo[pnum].iotype, + ctype, stype, coldef, + q->paraminfo[pnum].scale, + valp, vlen, &q->paraminfo[pnum].rlen), + msgp, "SQLBindParameter(%d)", pnum + 1)) +#endif + { + if (retry) { + retry = 0; + if (stype == SQL_VARCHAR) { + /* maybe MS Jet memo field */ + stype = SQL_LONGVARCHAR; + goto retry; + } +#ifdef UNICODE + if (stype == SQL_WVARCHAR) { + stype = SQL_WLONGVARCHAR; + goto retry; + } +#endif + } + return -1; + } + return 0; +} + +static VALUE +stmt_exec_int(int argc, VALUE *argv, VALUE self, int mode) +{ + STMT *q; + int i, argnum, has_out_parms = 0; + char *msg = NULL; + SQLRETURN ret; + + Data_Get_Struct(self, STMT, q); + if (argc > q->nump - ((EXEC_PARMXOUT(mode) < 0) ? 0 : 1)) { + rb_raise(Cerror, "%s", set_err("Too much parameters", 0)); + } + if (q->hstmt == SQL_NULL_HSTMT) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), + &msg, "SQLFreeStmt(SQL_CLOSE)")) { + goto error; + } + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_RESET_PARAMS), + "SQLFreeStmt(SQL_RESET_PARMS)"); + for (i = argnum = 0; i < q->nump; i++) { + VALUE arg; + + if (i == EXEC_PARMXOUT(mode)) { + if (bind_one_param(i, Qnil, q, &msg, &has_out_parms) < 0) { + goto error; + } + continue; + } + arg = (argnum < argc) ? argv[argnum++] : Qnil; + if (bind_one_param(i, arg, q, &msg, &has_out_parms) < 0) { + goto error; + } + } + ret = SQLEXECUTE(q->hstmt); + if (!succeeded_nodata(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &msg, "SQLExecute")) { +error: +#ifdef UNICODE + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].tofree != NULL) { + uc_free(q->paraminfo[i].tofree); + q->paraminfo[i].tofree = NULL; + } + } +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].tofree != NULL) { + uc_free(q->paraminfo[i].tofree); + q->paraminfo[i].tofree = NULL; + } + } +#endif + if (!has_out_parms) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_RESET_PARAMS), + "SQLFreeStmt(SQL_RESET_PARAMS)"); + } + if (ret == SQL_NO_DATA) { + return Qnil; + } + return make_result(q->dbc, q->hstmt, self, mode); +} + +static VALUE +stmt_exec(int argc, VALUE *argv, VALUE self) +{ + return stmt_exec_int(argc, argv, self, MAKERES_BLOCK); +} + +static VALUE +stmt_run(int argc, VALUE *argv, VALUE self) +{ + if (argc < 1) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + if (argc == 1) { + return stmt_prep_int(1, argv, self, + MAKERES_EXECD | MAKERES_BLOCK); + } + return stmt_exec(argc - 1, argv + 1, stmt_prep_int(1, argv, self, 0)); +} + +static VALUE +stmt_do(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt; + + if (argc < 1) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + if (argc == 1) { + stmt = stmt_prep_int(1, argv, self, + MAKERES_EXECD | MAKERES_BLOCK | MAKERES_NOCLOSE); + } else { + stmt = stmt_prep_int(1, argv, self, 0); + stmt_exec_int(argc - 1, argv + 1, stmt, + MAKERES_BLOCK | MAKERES_NOCLOSE); + } + return rb_ensure(stmt_nrows, stmt, stmt_drop, stmt); +} + +static VALUE +stmt_ignorecase(int argc, VALUE *argv, VALUE self) +{ + VALUE onoff = Qnil; + int *flag = NULL; + + rb_scan_args(argc, argv, "01", &onoff); + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + flag = &q->upc; + } else if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *p; + + Data_Get_Struct(self, DBC, p); + flag = &p->upc; + } else { + rb_raise(rb_eTypeError, "ODBC::Statement or ODBC::Database expected"); + return Qnil; + } + if (argc > 0) { + *flag = RTEST(onoff); + } + return *flag ? Qtrue : Qfalse; +} + +/* + *---------------------------------------------------------------------- + * + * Create statement without implicit SQL prepare or execute. + * + *---------------------------------------------------------------------- + */ +static VALUE +stmt_new(VALUE self) +{ + DBC *p; + SQLHSTMT hstmt; + char *msg = NULL; + + Data_Get_Struct(self, DBC, p); + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + return wrap_stmt(self, p, hstmt, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * Procedures with statements. + * + *---------------------------------------------------------------------- + */ + +static VALUE +stmt_proc_init(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt = (argc > 0) ? argv[0] : Qnil; + + if (rb_obj_is_kind_of(stmt, Cstmt) == Qtrue) { + rb_iv_set(self, "@statement", stmt); + rb_iv_set(self, "@return_output_param", (argc > 1) ? argv[1] : Qnil); + return self; + } + rb_raise(rb_eTypeError, "need ODBC::Statement as argument"); + return Qnil; +} + +static VALUE +stmt_proc_call(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt, val; + + stmt = rb_iv_get(self, "@statement"); + val = rb_iv_get(self, "@return_output_param"); + if (RTEST(val)) { + int parnum = NUM2INT(val); + + stmt_exec_int(argc, argv, stmt, EXEC_PARMXNULL(parnum)); + rb_call_super(1, &stmt); + return stmt_param_output_value(1, &val, stmt); + } + stmt_exec_int(argc, argv, stmt, 0); + return rb_call_super(1, &stmt); +} + +static VALUE +stmt_proc(int argc, VALUE *argv, VALUE self) +{ + VALUE sql, ptype, psize, pnum = Qnil, stmt, args[2]; + int parnum = 0; + + rb_scan_args(argc, argv, "13", &sql, &ptype, &psize, &pnum); + if (!rb_block_given_p()) { + rb_raise(rb_eArgError, "block required"); + } + stmt = stmt_prep_int(1, &sql, self, 0); + if (argc == 1) { + return rb_funcall(Cproc, IDnew, 1, stmt); + } + if ((argc < 4) || (pnum == Qnil)) { + pnum = INT2NUM(parnum); + } else { + parnum = NUM2INT(pnum); + } + args[0] = pnum; + args[1] = INT2NUM(SQL_PARAM_OUTPUT); + stmt_param_iotype(2, args, stmt); + args[1] = ptype; + stmt_param_output_type(2, args, stmt); + if (argc > 2) { + args[1] = psize; + } else { + args[1] = INT2NUM(256); + } + stmt_param_output_size(2, args, stmt); + return rb_funcall(Cproc, IDnew, 2, stmt, pnum); +} + +static VALUE +stmt_procwrap(int argc, VALUE *argv, VALUE self) +{ + VALUE arg0 = Qnil, arg1 = Qnil; + + rb_scan_args(argc, argv, "02", &arg0, &arg1); + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + if (arg1 != Qnil) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + arg1 = arg0; + arg0 = self; + } else if (rb_obj_is_kind_of(arg0, Cstmt) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Statement as 1st argument"); + } + return rb_funcall(Cproc, IDnew, 2, arg0, arg1); +} + +/* + *---------------------------------------------------------------------- + * + * Module functions. + * + *---------------------------------------------------------------------- + */ + +static VALUE +mod_dbcdisc(VALUE dbc) +{ + return dbc_disconnect(0, NULL, dbc); +} + +static VALUE +mod_connect(int argc, VALUE *argv, VALUE self) +{ + VALUE dbc = dbc_new(argc, argv, self); + + if (rb_block_given_p()) { + return rb_ensure(rb_yield, dbc, mod_dbcdisc, dbc); + } + return dbc; +} + +static VALUE +mod_2time(int argc, VALUE *argv, VALUE self) +{ + VALUE a1, a2; + VALUE y, m, d, hh, mm, ss, us; + int once = 0; + + rb_scan_args(argc, argv, "11", &a1, &a2); +again: + if (rb_obj_is_kind_of(a1, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments(2 for 1)"); + } + Data_Get_Struct(a1, TIMESTAMP_STRUCT, ts); + y = INT2NUM(ts->year); + m = INT2NUM(ts->month); + d = INT2NUM(ts->day); + hh = INT2NUM(ts->hour); + mm = INT2NUM(ts->minute); + ss = INT2NUM(ts->second); + us = INT2NUM(ts->fraction / 1000); + goto mktime; + } + if (rb_obj_is_kind_of(a1, Cdate) == Qtrue) { + DATE_STRUCT *date; + + if (a2 != Qnil) { + if (rb_obj_is_kind_of(a2, Ctime) == Qtrue) { + TIME_STRUCT *time; + + Data_Get_Struct(a2, TIME_STRUCT, time); + hh = INT2NUM(time->hour); + mm = INT2NUM(time->minute); + ss = INT2NUM(time->second); + } else { + rb_raise(rb_eTypeError, "expecting ODBC::Time"); + } + } else { + hh = INT2FIX(0); + mm = INT2FIX(0); + ss = INT2FIX(0); + } + Data_Get_Struct(a1, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + us = INT2FIX(0); + } + if (rb_obj_is_kind_of(a1, Ctime) == Qtrue) { + TIME_STRUCT *time; + + if (a2 != Qnil) { + if (rb_obj_is_kind_of(a2, Cdate) == Qtrue) { + DATE_STRUCT *date; + + Data_Get_Struct(a2, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + } else { + rb_raise(rb_eTypeError, "expecting ODBC::Date"); + } + } else { + VALUE now = FUNCALL_NOARGS(rb_cTime, IDnow); + + y = rb_funcall(rb_cTime, IDyear, 1, now); + m = rb_funcall(rb_cTime, IDmonth, 1, now); + d = rb_funcall(rb_cTime, IDday, 1, now); + } + Data_Get_Struct(a1, TIME_STRUCT, time); + hh = INT2NUM(time->hour); + mm = INT2NUM(time->minute); + ss = INT2NUM(time->second); + us = INT2FIX(0); +mktime: + return rb_funcall(rb_cTime, IDlocal, 7, y, m, d, hh, mm, ss, us); + } + if ((!once) && ((m = timestamp_load1(Ctimestamp, a1, -1)) != Qnil)) { + a1 = m; + once++; + goto again; + } + if ((!once) && ((m = date_load1(Cdate, a1, -1)) != Qnil)) { + a1 = m; + if ((argc > 1) && ((m = time_load1(Ctime, a2, -1)) != Qnil)) { + a2 = m; + } + once++; + goto again; + } + if ((!once) && ((m = time_load1(Ctime, a1, -1)) != Qnil)) { + a1 = m; + if ((argc > 1) && ((m = date_load1(Cdate, a2, -1)) != Qnil)) { + a2 = m; + } + once++; + goto again; + } + rb_raise(rb_eTypeError, + "expecting ODBC::TimeStamp or ODBC::Date/Time or String"); + return Qnil; +} + +static VALUE +mod_2date(VALUE self, VALUE arg) +{ + VALUE y, m, d; + int once = 0; + +again: + if (rb_obj_is_kind_of(arg, Cdate) == Qtrue) { + DATE_STRUCT *date; + + Data_Get_Struct(arg, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + goto mkdate; + } + if (rb_obj_is_kind_of(arg, Ctimestamp) == Qtrue){ + TIMESTAMP_STRUCT *ts; + + Data_Get_Struct(arg, TIMESTAMP_STRUCT, ts); + y = INT2NUM(ts->year); + m = INT2NUM(ts->month); + d = INT2NUM(ts->day); +mkdate: + return rb_funcall(rb_cDate, IDnew, 3, y, m, d); + } + if ((!once) && + (((m = date_load1(Cdate, arg, -1)) != Qnil) || + ((m = timestamp_load1(Ctimestamp, arg, -1)) != Qnil))) { + arg = m; + once++; + goto again; + } + rb_raise(rb_eTypeError, "expecting ODBC::Date/Timestamp or String"); + return Qnil; +} + +static VALUE +mod_trace(int argc, VALUE *argv, VALUE self) +{ + VALUE v = Qnil; + + rb_scan_args(argc, argv, "01", &v); +#ifdef TRACING + if (argc > 0) { + tracing = NUM2INT(v); + } + return INT2NUM(tracing); +#else + return INT2NUM(0); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Table of constants and intern'ed string mappings. + * + *---------------------------------------------------------------------- + */ + +#define O_CONST(x) { #x, x } +#define O_CONSTU(x) { #x, SQL_UNKNOWN_TYPE } +#define O_CONST2(x,y) { #x, y } +#define O_CONST_END { NULL, -1 } + +static struct { + const char *name; + int value; +} o_const[] = { + O_CONST(SQL_CURSOR_FORWARD_ONLY), + O_CONST(SQL_CURSOR_KEYSET_DRIVEN), + O_CONST(SQL_CURSOR_DYNAMIC), + O_CONST(SQL_CURSOR_STATIC), + O_CONST(SQL_CONCUR_READ_ONLY), + O_CONST(SQL_CONCUR_LOCK), + O_CONST(SQL_CONCUR_ROWVER), + O_CONST(SQL_CONCUR_VALUES), + O_CONST(SQL_FETCH_NEXT), + O_CONST(SQL_FETCH_FIRST), + O_CONST(SQL_FETCH_LAST), + O_CONST(SQL_FETCH_PRIOR), + O_CONST(SQL_FETCH_ABSOLUTE), + O_CONST(SQL_FETCH_RELATIVE), + O_CONST(SQL_UNKNOWN_TYPE), + O_CONST(SQL_CHAR), + O_CONST(SQL_NUMERIC), + O_CONST(SQL_DECIMAL), + O_CONST(SQL_INTEGER), + O_CONST(SQL_SMALLINT), + O_CONST(SQL_FLOAT), + O_CONST(SQL_REAL), + O_CONST(SQL_DOUBLE), + O_CONST(SQL_VARCHAR), +#ifdef SQL_DATETIME + O_CONST(SQL_DATETIME), +#else + O_CONSTU(SQL_DATETIME), +#endif +#ifdef SQL_DATE + O_CONST(SQL_DATE), +#else + O_CONSTU(SQL_DATE), +#endif +#ifdef SQL_TYPE_DATE + O_CONST(SQL_TYPE_DATE), +#else + O_CONSTU(SQL_TYPE_DATE), +#endif +#ifdef SQL_TIME + O_CONST(SQL_TIME), +#else + O_CONSTU(SQL_TIME), +#endif +#ifdef SQL_TYPE_TIME + O_CONST(SQL_TYPE_TIME), +#else + O_CONSTU(SQL_TYPE_TIME), +#endif +#ifdef SQL_TIMESTAMP + O_CONST(SQL_TIMESTAMP), +#else + O_CONSTU(SQL_TIMESTAMP), +#endif +#ifdef SQL_TYPE_TIMESTAMP + O_CONST(SQL_TYPE_TIMESTAMP), +#else + O_CONSTU(SQL_TYPE_TIMESTAMP), +#endif +#ifdef SQL_LONGVARCHAR + O_CONST(SQL_LONGVARCHAR), +#else + O_CONSTU(SQL_LONGVARCHAR), +#endif +#ifdef SQL_BINARY + O_CONST(SQL_BINARY), +#else + O_CONSTU(SQL_BINARY), +#endif +#ifdef SQL_VARBINARY + O_CONST(SQL_VARBINARY), +#else + O_CONSTU(SQL_VARBINARY), +#endif +#ifdef SQL_LONGVARBINARY + O_CONST(SQL_LONGVARBINARY), +#else + O_CONSTU(SQL_LONGVARBINARY), +#endif +#ifdef SQL_BIGINT + O_CONST(SQL_BIGINT), +#else + O_CONSTU(SQL_BIGINT), +#endif +#ifdef SQL_TINYINT + O_CONST(SQL_TINYINT), +#else + O_CONSTU(SQL_TINYINT), +#endif +#ifdef SQL_BIT + O_CONST(SQL_BIT), +#else + O_CONSTU(SQL_BIT), +#endif +#ifdef SQL_GUID + O_CONST(SQL_GUID), +#else + O_CONSTU(SQL_GUID), +#endif +#ifdef SQL_WCHAR + O_CONST(SQL_WCHAR), +#else + O_CONSTU(SQL_WCHAR), +#endif +#ifdef SQL_WVARCHAR + O_CONST(SQL_WVARCHAR), +#else + O_CONSTU(SQL_WVARCHAR), +#endif +#ifdef SQL_WLONGVARCHAR + O_CONST(SQL_WLONGVARCHAR), +#else + O_CONSTU(SQL_WLONGVARCHAR), +#endif +#ifdef SQL_ATTR_ODBC_VERSION + O_CONST(SQL_OV_ODBC2), + O_CONST(SQL_OV_ODBC3), +#else + O_CONST2(SQL_OV_ODBC2, 2), + O_CONST2(SQL_OV_ODBC3, 3), +#endif +#ifdef SQL_ATTR_CONNECTION_POOLING +#ifdef SQL_CP_OFF + O_CONST(SQL_CP_OFF), +#else + O_CONST2(SQL_CP_OFF, 0), +#endif +#ifdef SQL_CP_ONE_PER_DRIVER + O_CONST(SQL_CP_ONE_PER_DRIVER), +#else + O_CONST2(SQL_CP_ONE_PER_DRIVER, 1), +#endif +#ifdef SQL_CP_ONE_PER_HENV + O_CONST(SQL_CP_ONE_PER_HENV), +#else + O_CONST2(SQL_CP_ONE_PER_HENV, 2), +#endif +#ifdef SQL_CP_DEFAULT + O_CONST(SQL_CP_DEFAULT), +#else + O_CONST2(SQL_CP_DEFAULT, 0), +#endif +#else + O_CONST2(SQL_CP_OFF, 0), + O_CONST2(SQL_CP_ONE_PER_DRIVER, 0), + O_CONST2(SQL_CP_ONE_PER_HENV, 0), + O_CONST2(SQL_CP_DEFAULT, 0), +#endif +#ifdef SQL_ATTR_CP_MATCH + O_CONST(SQL_CP_STRICT_MATCH), + O_CONST(SQL_CP_RELAXED_MATCH), + O_CONST(SQL_CP_MATCH_DEFAULT), +#else + O_CONST2(SQL_CP_STRICT_MATCH, 0), + O_CONST2(SQL_CP_RELAXED_MATCH, 0), + O_CONST2(SQL_CP_MATCH_DEFAULT, 0), +#endif +#ifdef SQL_SCOPE_CURROW + O_CONST(SQL_SCOPE_CURROW), +#else + O_CONST2(SQL_SCOPE_CURROW, 0), +#endif +#ifdef SQL_SCOPE_TRANSACTION + O_CONST(SQL_SCOPE_TRANSACTION), +#else + O_CONST2(SQL_SCOPE_TRANSACTION, 0), +#endif +#ifdef SQL_SCOPE_SESSION + O_CONST(SQL_SCOPE_SESSION), +#else + O_CONST2(SQL_SCOPE_SESSION, 0), +#endif +#ifdef SQL_BEST_ROWID + O_CONST(SQL_BEST_ROWID), +#else + O_CONST2(SQL_BEST_ROWID, 0), +#endif +#ifdef SQL_ROWVER + O_CONST(SQL_ROWVER), +#else + O_CONST2(SQL_ROWVER, 0), +#endif + O_CONST(SQL_PARAM_TYPE_UNKNOWN), + O_CONST(SQL_PARAM_INPUT), + O_CONST(SQL_PARAM_OUTPUT), + O_CONST(SQL_PARAM_INPUT_OUTPUT), + O_CONST(SQL_DEFAULT_PARAM), + O_CONST(SQL_RETURN_VALUE), + O_CONST(SQL_RESULT_COL), + O_CONST(SQL_PT_UNKNOWN), + O_CONST(SQL_PT_PROCEDURE), + O_CONST(SQL_PT_FUNCTION), + + /* end of table */ + O_CONST_END +}; + +static struct { + ID *idp; + const char *str; +} ids[] = { + { &IDstart, "start" }, + { &IDatatinfo, "@@info" }, + { &IDataterror, "@@error" }, + { &IDkeys, "keys" }, + { &IDatattrs, "@attrs" }, + { &IDday, "day" }, + { &IDmonth, "month" }, + { &IDyear, "year" }, + { &IDmday, "mday" }, + { &IDnsec, "nsec" }, + { &IDusec, "usec" }, + { &IDsec, "sec" }, + { &IDmin, "min" }, + { &IDhour, "hour" }, + { &IDusec, "usec" }, + { &IDkeyp, "key?" }, + { &IDkey, "key" }, + { &IDSymbol, "Symbol" }, + { &IDString, "String" }, + { &IDFixnum, "Fixnum" }, + { &IDtable_names, "table_names" }, + { &IDnew, "new" }, + { &IDnow, "now" }, + { &IDlocal, "local" }, + { &IDname, "name" }, + { &IDtable, "table" }, + { &IDtype, "type" }, + { &IDlength, "length" }, + { &IDnullable, "nullable" }, + { &IDscale, "scale" }, + { &IDprecision, "precision" }, + { &IDsearchable, "searchable" }, + { &IDunsigned, "unsigned" }, + { &IDiotype, "iotype" }, + { &IDoutput_size, "output_size" }, + { &IDoutput_type, "output_type" }, + { &IDdescr, "descr" }, + { &IDstatement, "statement" }, + { &IDreturn_output_param, "return_output_param" }, + { &IDattrs, "attrs" }, + { &IDNULL, "NULL" }, + { &IDdefault, "default" }, +#ifdef USE_RB_ENC + { &IDencode, "encode" }, +#endif + { &IDparse, "parse" }, + { &IDutc, "utc" }, + { &IDlocal, "local" }, + { &IDto_s, "to_s" } +}; + +/* + *---------------------------------------------------------------------- + * + * Module initializer. + * + *---------------------------------------------------------------------- + */ + +void +#ifdef UNICODE +Init_odbc_utf8() +#else +Init_odbc() +#endif +{ + int i; + const char *modname = "ODBC"; + ID modid = rb_intern(modname); + VALUE v = Qnil; + + rb_require("date"); + rb_cDate = rb_eval_string("Date"); + + if (rb_const_defined(rb_cObject, modid)) { + v = rb_const_get(rb_cObject, modid); + if (TYPE(v) != T_MODULE) { + rb_raise(rb_eTypeError, "%s already defined", modname); + } + } + if (v != Qnil) { +#ifdef UNICODE + modname = "ODBC_UTF8"; +#else + modname = "ODBC_NONE"; +#endif + } + + for (i = 0; i < (int) (sizeof (ids) / sizeof (ids[0])); i++) { + *(ids[i].idp) = rb_intern(ids[i].str); + } + + Modbc = rb_define_module(modname); + Cobj = rb_define_class_under(Modbc, "Object", rb_cObject); + rb_define_class_variable(Cobj, "@@error", Qnil); + rb_define_class_variable(Cobj, "@@info", Qnil); + + Cenv = rb_define_class_under(Modbc, "Environment", Cobj); + Cdbc = rb_define_class_under(Modbc, "Database", Cenv); + Cstmt = rb_define_class_under(Modbc, "Statement", Cdbc); + rb_include_module(Cstmt, rb_mEnumerable); + + Ccolumn = rb_define_class_under(Modbc, "Column", Cobj); + rb_attr(Ccolumn, IDname, 1, 0, Qfalse); + rb_attr(Ccolumn, IDtable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDtype, 1, 0, Qfalse); + rb_attr(Ccolumn, IDlength, 1, 0, Qfalse); + rb_attr(Ccolumn, IDnullable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDscale, 1, 0, Qfalse); + rb_attr(Ccolumn, IDprecision, 1, 0, Qfalse); + rb_attr(Ccolumn, IDsearchable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDunsigned, 1, 0, Qfalse); + + Cparam = rb_define_class_under(Modbc, "Parameter", Cobj); + rb_attr(Cparam, IDtype, 1, 0, Qfalse); + rb_attr(Cparam, IDprecision, 1, 0, Qfalse); + rb_attr(Cparam, IDscale, 1, 0, Qfalse); + rb_attr(Cparam, IDnullable, 1, 0, Qfalse); + rb_attr(Cparam, IDiotype, 1, 0, Qfalse); + rb_attr(Cparam, IDoutput_size, 1, 0, Qfalse); + rb_attr(Cparam, IDoutput_type, 1, 0, Qfalse); + + Cdsn = rb_define_class_under(Modbc, "DSN", Cobj); + rb_attr(Cdsn, IDname, 1, 1, Qfalse); + rb_attr(Cdsn, IDdescr, 1, 1, Qfalse); + + Cdrv = rb_define_class_under(Modbc, "Driver", Cobj); + rb_attr(Cdrv, IDname, 1, 1, Qfalse); + rb_attr(Cdrv, IDattrs, 1, 1, Qfalse); + + Cerror = rb_define_class_under(Modbc, "Error", rb_eStandardError); + + Cproc = rb_define_class("ODBCProc", rb_cProc); + + Cdate = rb_define_class_under(Modbc, "Date", Cobj); + rb_include_module(Cdate, rb_mComparable); + Ctime = rb_define_class_under(Modbc, "Time", Cobj); + rb_include_module(Ctime, rb_mComparable); + Ctimestamp = rb_define_class_under(Modbc, "TimeStamp", Cobj); + rb_include_module(Ctimestamp, rb_mComparable); + + /* module functions */ + rb_define_module_function(Modbc, "trace", mod_trace, -1); + rb_define_module_function(Modbc, "trace=", mod_trace, -1); + rb_define_module_function(Modbc, "connect", mod_connect, -1); + rb_define_module_function(Modbc, "datasources", dbc_dsns, 0); + rb_define_module_function(Modbc, "drivers", dbc_drivers, 0); + rb_define_module_function(Modbc, "error", dbc_error, 0); + rb_define_module_function(Modbc, "info", dbc_warn, 0); + rb_define_module_function(Modbc, "clear_error", dbc_clrerror, 0); + rb_define_module_function(Modbc, "newenv", env_new, 0); + rb_define_module_function(Modbc, "to_time", mod_2time, -1); + rb_define_module_function(Modbc, "to_date", mod_2date, 1); + rb_define_module_function(Modbc, "connection_pooling", env_cpooling, -1); + rb_define_module_function(Modbc, "connection_pooling=", env_cpooling, -1); + rb_define_module_function(Modbc, "raise", dbc_raise, 1); + + /* singleton methods and constructors */ + rb_define_singleton_method(Cobj, "error", dbc_error, 0); + rb_define_singleton_method(Cobj, "info", dbc_warn, 0); + rb_define_singleton_method(Cobj, "clear_error", dbc_clrerror, 0); + rb_define_singleton_method(Cobj, "raise", dbc_raise, 1); + rb_define_alloc_func(Cenv, env_new); + rb_define_singleton_method(Cenv, "connect", dbc_new, -1); +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Cdbc, dbc_alloc); +#else + rb_define_alloc_func(Cdbc, dbc_new); + rb_define_alloc_func(Cdsn, dsn_new); + rb_define_alloc_func(Cdrv, drv_new); +#endif + rb_define_method(Cdsn, "initialize", dsn_init, 0); + rb_define_method(Cdrv, "initialize", drv_init, 0); + rb_define_method(Cdbc, "newstmt", stmt_new, 0); + + /* common (Cobj) methods */ + rb_define_method(Cobj, "error", dbc_error, 0); + rb_define_method(Cobj, "info", dbc_warn, 0); + rb_define_method(Cobj, "clear_error", dbc_clrerror, 0); + rb_define_method(Cobj, "raise", dbc_raise, 1); + + /* common (Cenv) methods */ + rb_define_method(Cenv, "connect", dbc_new, -1); + rb_define_method(Cenv, "environment", env_of, 0); + rb_define_method(Cenv, "transaction", dbc_transaction, 0); + rb_define_method(Cenv, "commit", dbc_commit, 0); + rb_define_method(Cenv, "rollback", dbc_rollback, 0); + rb_define_method(Cenv, "connection_pooling", env_cpooling, -1); + rb_define_method(Cenv, "connection_pooling=", env_cpooling, -1); + rb_define_method(Cenv, "cp_match", env_cpmatch, -1); + rb_define_method(Cenv, "cp_match=", env_cpmatch, -1); + rb_define_method(Cenv, "odbc_version", env_odbcver, -1); + rb_define_method(Cenv, "odbc_version=", env_odbcver, -1); + + /* management things (odbcinst.h) */ + rb_define_module_function(Modbc, "add_dsn", dbc_adddsn, -1); + rb_define_module_function(Modbc, "config_dsn", dbc_confdsn, -1); + rb_define_module_function(Modbc, "del_dsn", dbc_deldsn, -1); + rb_define_module_function(Modbc, "write_file_dsn", dbc_wfdsn, -1); + rb_define_module_function(Modbc, "read_file_dsn", dbc_rfdsn, -1); + + /* connection (database) methods */ + rb_define_method(Cdbc, "initialize", dbc_connect, -1); + rb_define_method(Cdbc, "connect", dbc_connect, -1); + rb_define_method(Cdbc, "connected?", dbc_connected, 0); + rb_define_method(Cdbc, "drvconnect", dbc_drvconnect, 1); + rb_define_method(Cdbc, "drop_all", dbc_dropall, 0); + rb_define_method(Cdbc, "disconnect", dbc_disconnect, -1); + rb_define_method(Cdbc, "tables", dbc_tables, -1); + rb_define_method(Cdbc, "columns", dbc_columns, -1); + rb_define_method(Cdbc, "primary_keys", dbc_primkeys, -1); + rb_define_method(Cdbc, "indexes", dbc_indexes, -1); + rb_define_method(Cdbc, "types", dbc_types, -1); + rb_define_method(Cdbc, "foreign_keys", dbc_forkeys, -1); + rb_define_method(Cdbc, "table_privileges", dbc_tpriv, -1); + rb_define_method(Cdbc, "procedures", dbc_procs, -1); + rb_define_method(Cdbc, "procedure_columns", dbc_proccols, -1); + rb_define_method(Cdbc, "special_columns", dbc_speccols, -1); + rb_define_method(Cdbc, "get_info", dbc_getinfo, -1); + rb_define_method(Cdbc, "prepare", stmt_prep, -1); + rb_define_method(Cdbc, "run", stmt_run, -1); + rb_define_method(Cdbc, "do", stmt_do, -1); + rb_define_method(Cdbc, "proc", stmt_proc, -1); + rb_define_method(Cdbc, "use_time", dbc_timefmt, -1); + rb_define_method(Cdbc, "use_time=", dbc_timefmt, -1); + rb_define_method(Cdbc, "use_utc", dbc_timeutc, -1); + rb_define_method(Cdbc, "use_utc=", dbc_timeutc, -1); + rb_define_method(Cdbc, "use_sql_column_name", dbc_use_scn, -1); + rb_define_method(Cdbc, "use_sql_column_name=", dbc_use_scn, -1); + + /* connection options */ + rb_define_method(Cdbc, "get_option", dbc_getsetoption, -1); + rb_define_method(Cdbc, "set_option", dbc_getsetoption, -1); + rb_define_method(Cdbc, "autocommit", dbc_autocommit, -1); + rb_define_method(Cdbc, "autocommit=", dbc_autocommit, -1); + rb_define_method(Cdbc, "concurrency", dbc_concurrency, -1); + rb_define_method(Cdbc, "concurrency=", dbc_concurrency, -1); + rb_define_method(Cdbc, "maxrows", dbc_maxrows, -1); + rb_define_method(Cdbc, "maxrows=", dbc_maxrows, -1); + rb_define_method(Cdbc, "timeout", dbc_timeout, -1); + rb_define_method(Cdbc, "timeout=", dbc_timeout, -1); + rb_define_method(Cdbc, "login_timeout", dbc_login_timeout, -1); + rb_define_method(Cdbc, "login_timeout=", dbc_login_timeout, -1); + rb_define_method(Cdbc, "maxlength", dbc_maxlength, -1); + rb_define_method(Cdbc, "maxlength=", dbc_maxlength, -1); + rb_define_method(Cdbc, "rowsetsize", dbc_rowsetsize, -1); + rb_define_method(Cdbc, "cursortype", dbc_cursortype, -1); + rb_define_method(Cdbc, "cursortype=", dbc_cursortype, -1); + rb_define_method(Cdbc, "noscan", dbc_noscan, -1); + rb_define_method(Cdbc, "noscan=", dbc_noscan, -1); + rb_define_method(Cdbc, "ignorecase", stmt_ignorecase, -1); + rb_define_method(Cdbc, "ignorecase=", stmt_ignorecase, -1); + + /* statement methods */ + rb_define_method(Cstmt, "drop", stmt_drop, 0); + rb_define_method(Cstmt, "close", stmt_close, 0); + rb_define_method(Cstmt, "cancel", stmt_cancel, 0); + rb_define_method(Cstmt, "column", stmt_column, -1); + rb_define_method(Cstmt, "columns", stmt_columns, -1); + rb_define_method(Cstmt, "parameter", stmt_param, -1); + rb_define_method(Cstmt, "parameters", stmt_params, 0); + rb_define_method(Cstmt, "param_type", stmt_param_type, -1); + rb_define_method(Cstmt, "param_iotype", stmt_param_iotype, -1); + rb_define_method(Cstmt, "param_output_size", stmt_param_output_size, -1); + rb_define_method(Cstmt, "param_output_type", stmt_param_output_type, -1); + rb_define_method(Cstmt, "param_output_value", stmt_param_output_value, -1); + rb_define_method(Cstmt, "ncols", stmt_ncols, 0); + rb_define_method(Cstmt, "nrows", stmt_nrows, 0); + rb_define_method(Cstmt, "nparams", stmt_nparams, 0); + rb_define_method(Cstmt, "cursorname", stmt_cursorname, -1); + rb_define_method(Cstmt, "cursorname=", stmt_cursorname, -1); + rb_define_method(Cstmt, "fetch", stmt_fetch, 0); + rb_define_method(Cstmt, "fetch!", stmt_fetch_bang, 0); + rb_define_method(Cstmt, "fetch_first", stmt_fetch_first, 0); + rb_define_method(Cstmt, "fetch_first!", stmt_fetch_first_bang, 0); + rb_define_method(Cstmt, "fetch_scroll", stmt_fetch_scroll, -1); + rb_define_method(Cstmt, "fetch_scroll!", stmt_fetch_scroll_bang, -1); + rb_define_method(Cstmt, "fetch_hash", stmt_fetch_hash, -1); + rb_define_method(Cstmt, "fetch_hash!", stmt_fetch_hash_bang, -1); + rb_define_method(Cstmt, "fetch_first_hash", stmt_fetch_first_hash, -1); + rb_define_method(Cstmt, "fetch_many", stmt_fetch_many, 1); + rb_define_method(Cstmt, "fetch_all", stmt_fetch_all, 0); + rb_define_method(Cstmt, "each", stmt_each, 0); + rb_define_method(Cstmt, "each_hash", stmt_each_hash, -1); + rb_define_method(Cstmt, "execute", stmt_exec, -1); + rb_define_method(Cstmt, "make_proc", stmt_procwrap, -1); + rb_define_method(Cstmt, "more_results", stmt_more_results, 0); + rb_define_method(Cstmt, "prepare", stmt_prep, -1); + rb_define_method(Cstmt, "run", stmt_run, -1); + rb_define_singleton_method(Cstmt, "make_proc", stmt_procwrap, -1); + + /* statement options */ + rb_define_method(Cstmt, "get_option", stmt_getsetoption, -1); + rb_define_method(Cstmt, "set_option", stmt_getsetoption, -1); + rb_define_method(Cstmt, "concurrency", stmt_concurrency, -1); + rb_define_method(Cstmt, "concurrency=", stmt_concurrency, -1); + rb_define_method(Cstmt, "maxrows", stmt_maxrows, -1); + rb_define_method(Cstmt, "maxrows=", stmt_maxrows, -1); + rb_define_method(Cstmt, "timeout", stmt_timeout, -1); + rb_define_method(Cstmt, "timeout=", stmt_timeout, -1); + rb_define_method(Cstmt, "maxlength", stmt_maxlength, -1); + rb_define_method(Cstmt, "maxlength=", stmt_maxlength, -1); + rb_define_method(Cstmt, "cursortype", stmt_cursortype, -1); + rb_define_method(Cstmt, "cursortype=", stmt_cursortype, -1); + rb_define_method(Cstmt, "noscan", stmt_noscan, -1); + rb_define_method(Cstmt, "noscan=", stmt_noscan, -1); + rb_define_method(Cstmt, "rowsetsize", stmt_rowsetsize, -1); + + /* data type methods */ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Cdate, date_alloc); +#else + rb_define_singleton_method(Cdate, "new", date_new, -1); +#endif + rb_define_singleton_method(Cdate, "_load", date_load, 1); + rb_define_method(Cdate, "initialize", date_init, -1); + rb_define_method(Cdate, "clone", date_clone, 0); + rb_define_method(Cdate, "to_s", date_to_s, 0); + rb_define_method(Cdate, "_dump", date_dump, 1); + rb_define_method(Cdate, "inspect", date_inspect, 0); + rb_define_method(Cdate, "year", date_year, -1); + rb_define_method(Cdate, "month", date_month, -1); + rb_define_method(Cdate, "day", date_day, -1); + rb_define_method(Cdate, "year=", date_year, -1); + rb_define_method(Cdate, "month=", date_month, -1); + rb_define_method(Cdate, "day=", date_day, -1); + rb_define_method(Cdate, "<=>", date_cmp, 1); + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Ctime, time_alloc); +#else + rb_define_singleton_method(Ctime, "new", time_new, -1); +#endif + rb_define_singleton_method(Ctime, "_load", time_load, 1); + rb_define_method(Ctime, "initialize", time_init, -1); + rb_define_method(Ctime, "clone", time_clone, 0); + rb_define_method(Ctime, "to_s", time_to_s, 0); + rb_define_method(Ctime, "_dump", time_dump, 1); + rb_define_method(Ctime, "inspect", time_inspect, 0); + rb_define_method(Ctime, "hour", time_hour, -1); + rb_define_method(Ctime, "minute", time_min, -1); + rb_define_method(Ctime, "second", time_sec, -1); + rb_define_method(Ctime, "hour=", time_hour, -1); + rb_define_method(Ctime, "minute=", time_min, -1); + rb_define_method(Ctime, "second=", time_sec, -1); + rb_define_method(Ctime, "<=>", time_cmp, 1); + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Ctimestamp, timestamp_alloc); +#else + rb_define_singleton_method(Ctimestamp, "new", timestamp_new, -1); +#endif + rb_define_singleton_method(Ctimestamp, "_load", timestamp_load, 1); + rb_define_method(Ctimestamp, "initialize", timestamp_init, -1); + rb_define_method(Ctimestamp, "clone", timestamp_clone, 0); + rb_define_method(Ctimestamp, "to_s", timestamp_to_s, 0); + rb_define_method(Ctimestamp, "_dump", timestamp_dump, 1); + rb_define_method(Ctimestamp, "inspect", timestamp_inspect, 0); + rb_define_method(Ctimestamp, "year", timestamp_year, -1); + rb_define_method(Ctimestamp, "month", timestamp_month, -1); + rb_define_method(Ctimestamp, "day", timestamp_day, -1); + rb_define_method(Ctimestamp, "hour", timestamp_hour, -1); + rb_define_method(Ctimestamp, "minute", timestamp_min, -1); + rb_define_method(Ctimestamp, "second", timestamp_sec, -1); + rb_define_method(Ctimestamp, "fraction", timestamp_fraction, -1); + rb_define_method(Ctimestamp, "year=", timestamp_year, -1); + rb_define_method(Ctimestamp, "month=", timestamp_month, -1); + rb_define_method(Ctimestamp, "day=", timestamp_day, -1); + rb_define_method(Ctimestamp, "hour=", timestamp_hour, -1); + rb_define_method(Ctimestamp, "minute=", timestamp_min, -1); + rb_define_method(Ctimestamp, "second=", timestamp_sec, -1); + rb_define_method(Ctimestamp, "fraction=", timestamp_fraction, -1); + rb_define_method(Ctimestamp, "<=>", timestamp_cmp, 1); + + /* procedure methods */ + rb_define_method(Cproc, "initialize", stmt_proc_init, -1); + rb_define_method(Cproc, "call", stmt_proc_call, -1); + rb_define_method(Cproc, "[]", stmt_proc_call, -1); + rb_attr(Cproc, IDstatement, 1, 0, Qfalse); + rb_attr(Cproc, IDreturn_output_param, 1, 0, Qfalse); +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC + rb_enable_super(Cproc, "call"); + rb_enable_super(Cproc, "[]"); +#endif + + /* constants */ + for (i = 0; o_const[i].name != NULL; i++) { + rb_define_const(Modbc, o_const[i].name, + INT2NUM(o_const[i].value)); + } + for (i = 0; get_info_map[i].name != NULL; i++) { + rb_define_const(Modbc, get_info_map[i].name, + INT2NUM(get_info_map[i].info)); + } + for (i = 0; get_info_bitmap[i].name != NULL; i++) { + rb_define_const(Modbc, get_info_bitmap[i].name, + INT2NUM(get_info_bitmap[i].bits)); + } + for (i = 0; option_map[i].name != NULL; i++) { + rb_define_const(Modbc, option_map[i].name, + INT2NUM(option_map[i].option)); + } + +#ifdef UNICODE + rb_define_const(Modbc, "UTF8", Qtrue); +#ifdef USE_RB_ENC + rb_enc = rb_utf8_encoding(); + rb_encv = rb_enc_from_encoding(rb_enc); +#endif +#else + rb_define_const(Modbc, "UTF8", Qfalse); +#endif + +#ifdef TRACING + if (ruby_verbose) { + tracing = -1; + } +#endif +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * tab-width: 8 + * End: + */ diff --git a/ext/utf8/extconf.rb b/ext/utf8/extconf.rb new file mode 100644 index 0000000..26ef8c9 --- /dev/null +++ b/ext/utf8/extconf.rb @@ -0,0 +1,158 @@ +require 'mkmf' + +if ! defined? PLATFORM + PLATFORM = RUBY_PLATFORM +end + +def have_library_ex(lib, func="main", headers=nil) + checking_for "#{func}() in -l#{lib}" do + libs = append_library($libs, lib) + if !func.nil? && !func.empty? && COMMON_LIBS.include?(lib) + true + elsif try_func(func, libs, headers) + $libs = libs + true + else + false + end + end +end + +def try_func_nolink(func, libs, headers = nil, &b) + headers = cpp_include(headers) + try_compile(<<"SRC", libs, &b) +#{COMMON_HEADERS} +#{headers} +/*top*/ +int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; } +SRC +end + +def have_func_nolink(func, headers = nil, &b) + checking_for "#{func}()" do + if try_func_nolink(func, $libs, headers, &b) + $defs.push(format("-DHAVE_%s", func.upcase)) + true + else + false + end + end +end + +dir_config("odbc") +have_header("version.h") +have_header("ruby/version.h") +have_header("sql.h") || begin + puts "ERROR: sql.h not found" + exit 1 +end +have_header("sqlext.h") || begin + puts "ERROR: sqlext.h not found" + exit 1 +end +testdlopen = enable_config("dlopen", false) +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLTCHAR", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLTCHAR manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLTCHAR" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLLEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLLEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLLEN" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLULEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLULEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLULEN" +end +$have_odbcinst_h = have_header("odbcinst.h") + +if PLATFORM =~ /mswin32/ then + if !have_library_ex("odbc32", "SQLAllocConnect", "sql.h") || + !have_library_ex("odbccp32", "SQLConfigDataSource", "odbcinst.h") || + !have_library_ex("odbccp32", "SQLInstallerError", "odbcinst.h") || + !have_library("user32", "CharUpper") then + puts "Can not locate odbc libraries" + exit 1 + end + have_func("SQLConfigDataSourceW", "odbcinst.h") + have_func("SQLWriteFileDSNW", "odbcinst.h") + have_func("SQLReadFileDSNW", "odbcinst.h") + have_func("SQLInstallerError", "odbcinst.h") + have_func("SQLInstallerErrorW", "odbcinst.h") +elsif PLATFORM =~ /(mingw|cygwin)/ then + have_library("odbc32") + have_library("odbccp32") + have_library("user32") + header = ["windows.h", "odbcinst.h"] + have_func("SQLConfigDataSourceW", header) + have_func("SQLWriteFileDSNW", header) + have_func("SQLReadFileDSNW", header) + have_func("SQLInstallerError", header) + have_func("SQLInstallerErrorW", header) +elsif (testdlopen && PLATFORM !~ /(macos|darwin)/ && CONFIG["CC"] =~ /gcc/ && have_func("dlopen", "dlfcn.h") && have_library("dl", "dlopen")) then + $LDFLAGS+=" -Wl,-init -Wl,ruby_odbc_init -Wl,-fini -Wl,ruby_odbc_fini" + $CPPFLAGS+=" -DHAVE_SQLCONFIGDATASOURCE" + $CPPFLAGS+=" -DHAVE_SQLINSTALLERERROR" + $CPPFLAGS+=" -DUSE_DLOPEN_FOR_ODBC_LIBS" + # but test the UNICODE installer functions w/o linking + # in case we need to provide fwd declarations + have_func_nolink("SQLConfigDataSourceW", "odbcinst.h") + have_func_nolink("SQLWriteFileDSNW", "odbcinst.h") + have_func_nolink("SQLReadFileDSNW", "odbcinst.h") + have_func_nolink("SQLInstallerErrorW", "odbcinst.h") +else + $CPPFLAGS+=" -DUNICODE -D_UNICODE" + have_library("odbc", "SQLAllocConnect") || + have_library("iodbc", "SQLAllocConnect") + ($have_odbcinst_h && + have_library("odbcinst", "SQLConfigDataSource")) || + ($have_odbcinst_h && + have_library("iodbcinst", "SQLConfigDataSource")) + $have_odbcinst_h && + have_func("SQLConfigDataSourceW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLWriteFileDSNW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLReadFileDSNW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLInstallerError", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLInstallerErrorW", "odbcinst.h") +end + +create_makefile("odbc_utf8") diff --git a/ext/utf8/init.c b/ext/utf8/init.c new file mode 100644 index 0000000..1bf2e6b --- /dev/null +++ b/ext/utf8/init.c @@ -0,0 +1,12 @@ +/* + * Part of ODBC-Ruby binding + * Copyright (c) 2006-2007 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: init.c,v 1.2 2007/04/07 09:39:08 chw Exp chw $ + */ + +#include "../init.c" diff --git a/ext/utf8/odbc.c b/ext/utf8/odbc.c new file mode 100644 index 0000000..a222b9a --- /dev/null +++ b/ext/utf8/odbc.c @@ -0,0 +1,17 @@ +/* + * ODBC-Ruby binding + * Copyright (c) 2001-2011 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: odbc.c,v 1.6 2011/01/15 08:02:55 chw Exp chw $ + */ + +#undef UNICODE +#undef _UNICODE +#define UNICODE +#define _UNICODE + +#include "../odbc.c" diff --git a/lib/cqgen.rb b/lib/cqgen.rb new file mode 100644 index 0000000..ef2e79f --- /dev/null +++ b/lib/cqgen.rb @@ -0,0 +1,561 @@ +#!/usr/bin/env ruby +# +# cqgen.rb - Poor man's C code generator to automatically +# make access wrappers for ODBC datasources. +# +# ------------------------------------------------------------------- +# This file is part of "ODBC-Ruby binding" +# +# Copyright (c) 2006 Christian Werner +# +# See the file "COPYING" for information on usage +# and redistribution of this file and for a +# DISCLAIMER OF ALL WARRANTIES. +# ------------------------------------------------------------------- +# +# Pre-requisites: +# +# * Ruby 1.6 or 1.8 +# * Ruby/ODBC >= 0.996 +# * Acesss to a ODBC datasource with properly +# loaded schema for the wrappers to generate +# +# ------------------------------------------------------------------- +# +# Functions in this module +# +# cqgen DSName, UID, PWD, CFName, SQL, [ParmTypes, ParmNames] +# +# DSName: name of datasource to connect +# UID: user name on datasource +# PWD: password on datasource +# CFName: name for C function +# SQL: SQL text of one query yielding +# one result set +# ParmTypes: (optional, array) types for +# query parameters +# ParmNames: (optional, array) names for +# query parameters +# +# use this function to make C code for +# SELECT/UPDATE/DELETE/INSERT SQL statements. +# The "ParmTypes" is useful since many ODBC drivers +# cannot return the proper types in SQLDescribeParam(). +# +# cqgen_test +# append test functions and skeleton test main() +# to end of C file +# +# +# cqgen_output c_name, h_name +# write C source code, 'c_name' for functions, +# 'h_name' for optional header file +# +# ------------------------------------------------------------------- +# +# Specification file sample +# +# ---BOF--- +# require 'cqgen' +# # simple query +# cqgen 'bla', '', '', "GetResults", %q{ +# select * from results +# } +# # query with parameters using last DSN +# cqgen nil, nil, nil, "GetResultsGt", %q{ +# select * from results where funfactor > ? +# } +# # update with parameters using other DSN +# cqgen 'bla2', '', '', "IncrFunFactor", %q{ +# update results set funfactor = ? where funfactor < ? +# }, [ ODBC::SQL_INTEGER, ODBC::SQL_INTEGER ], +# [ "newfunfactor", "maxfunfactor" ] +# cqgen_test +# cqgen_output 'bla_wrap.c' +# ---EOF--- +# +# Run this file with a command line like +# +# $ ruby sample-file +# $ cc -c bla_wrap.c +# or +# $ cc -o bla_wrap_test bla_wrap.c -DTEST_cqgen_all -lodbc +# +# Depending on the SQL text, cqgen writes one or more +# C functions and zero or one structure typedefs. +# The structure typedefs are merged when possible, +# thus you should write the least specific queries +# on top of the specification file. +# +# In the sample: +# +# * function GetResults_init to initiate the query +# * function GetResults to retrieve one result row +# * function GetResults_deinit to release resources +# associated with the query +# * struct RESULT_GetResults for representation of +# one result row +# +# * function GetResultsGt_init with one parameter par1 +# * macro GetResultsGt aliased to GetResults +# (function reused from 1st query) +# * macro GetResultsGt_deinit aliased to GetResults_deinit +# (function reused from 1st query) +# * struct RESULT_GetResults for representation of +# one result row (struct name reused from 1st query) +# +# * function IncrFunFactor with two parameters named +# "newfunfactor" and "maxfunfactor" and an SQLINTEGER +# reference for the number of affected rows +# +# ------------------------------------------------------------------- + +require 'odbc' + +$cqgen_dsn=nil +$cqgen_uid=nil +$cqgen_pwd=nil +$cqgen_simple_fns=Hash.new +$cqgen_stypes=Hash.new +$cqgen_h1='' +$cqgen_h2='' +$cqgen_c='' +$cqgen_ct1='' +$cqgen_ct2='' + +def cqgen(dsn, uid, pwd, name, sql, parmtypes=nil, parmnames=nil) + dsn=$cqgen_dsn if dsn.nil? + uid=$cqgen_uid if uid.nil? + pwd=$cqgen_pwd if pwd.nil? + begin + conn=ODBC::connect(dsn, uid, pwd) + $cqgen_dsn=dsn + $cqgen_uid=uid + $cqgen_pwd=pwd + rescue Exception => err + $stderr.puts("connect for #{name} failed:\n") + $stderr.puts("\t#{err}\n") + return + end + sql = sql.strip + sql = sql.gsub(/[\r\n]+/, " ") + if sql.downcase =~ /where/ + sql0=sql + " and 1=0" + else + sql0=sql + " where 1=0" + end + begin + stmt=conn.prepare(sql0) + if stmt.ncols < 1 + stmt.execute + end + rescue Exception => err + $stderr.puts("SQL error for #{name}:\n") + $stderr.puts("\t#{err}\n") + return + end + if stmt.ncols > 0 + fhead="SQLRETURN\n" + name + "_init(SQLHDBC hdbc, SQLHSTMT *hstmtRet" + fhead2="SQLRETURN\n" + name + "(SQLHSTMT hstmt" + flvars2="{\n SQLRETURN ret;\n INTERNAL_STMT *istmt;\n" + fbody2="\n istmt = (INTERNAL_STMT *) hstmt;\n" + fhead3="SQLRETURN\n" + name + "_deinit(SQLHSTMT hstmt)\n" + flvars3="{\n INTERNAL_STMT *istmt;\n" + fbody3="\n istmt = (INTERNAL_STMT *) hstmt;\n" + fbody3+=" if (istmt != NULL) {\n" + fbody3+="\tSQLFreeStmt(istmt->hstmt, SQL_DROP);\n" + fbody3+="\tfree(istmt);\n" + fbody3+=" }\n return SQL_SUCCESS;\n}\n\n" + else + fhead="SQLRETURN\n" + name + "(SQLHDBC hdbc" + fhead2="" + flvars2="" + fbody2="" + fhead3="" + flvars3="" + fbody3="" + end + flvars="{\n SQLRETURN ret;\n" + if stmt.ncols < 1 + flvars+=" SQLHSTMT hstmt;\n" + end + fbody="" + flvarst="{\n SQLRETURN ret;\n" + fheadt="SQLRETURN\n" + name + "_test(SQLHDBC hdbc" + fbodyt="" + if stmt.ncols > 0 + flvars+=" INTERNAL_STMT *istmt;\n SQLHSTMT hstmt;\n" + fbody+=" *hstmtRet = hstmt = SQL_NULL_HSTMT;\n" + fbody+=" ret = SQLAllocStmt(hdbc, &hstmt);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody+=" istmt = malloc(sizeof (INTERNAL_STMT));\n" + fbody+=" if (istmt == NULL) {\n\tSQLFreeStmt(hstmt, SQL_DROP);\n" + fbody+="\treturn SQL_ERROR;\n }\n" + fbody+=" istmt->hstmt = hstmt;\n" + fbody+=" istmt->result = NULL;\n" + fbody+=" *hstmtRet = (SQLHSTMT) istmt;\n" + flvarst+=" SQLHSTMT hstmt;\n" + fbodyt+=" ret = #{name}_init(hdbc, &hstmt" + else + fbodyt+=" ret = #{name}(hdbc" + end + pnum=1 + stmt.parameters.each do |par| + if !parmtypes.nil? + ptype=parmtypes[pnum-1] + end + if ptype.nil? + ptype = par.type + end + if !parmnames.nil? + pname=parmnames[pnum-1] + end + if pname.nil? + pname="par#{pnum}" + end + case ptype + when ODBC::SQL_INTEGER + pdef=", SQLINTEGER *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLINTEGER);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_LONG, SQL_INTEGER, #{par.precision}, #{par.scale}," + when ODBC::SQL_SMALLINT + pdef=", SQLSMALLINT *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLSMALLINT);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_SHORT, SQL_SMALLINT, #{par.precision}, #{par.scale}," + when ODBC::SQL_FLOAT + pdef=", SQLDOUBLE *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLDOUBLE);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_DOUBLE, SQL_FLOAT, #{par.precision}, #{par.scale}," + when ODBC::SQL_DOUBLE + pdef=", SQLDOUBLE *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLDOUBLE);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_DOUBLE, SQL_DOUBLE, #{par.precision}, #{par.scale}," + else + pdef=", SQLCHAR *#{pname}" + fbody+=" #{pname}_len = #{pname} ? strlen(#{pname}) : SQL_NULL_DATA;\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_CHAR, SQL_CHAR, #{par.precision}, #{par.scale}," + end + fbody+=" #{pname}, #{pname}_len, &#{pname}_len);\n" + fhead+=pdef + fheadt+=pdef + fbodyt+=", #{pname}" + flvars+=" SQLINTEGER #{pname}_len;\n" + if stmt.ncols > 0 + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + else + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + end + pnum+=1 + end + fheadt+=")\n" + if stmt.ncols < 1 + fbodyt+= ", &rowCount" + end + fbodyt+=");\n" + if stmt.ncols > 0 + fbodyt+=" if (!SQL_SUCCEEDED(ret)) {\n" + fbodyt+="\t#{name}_deinit(hstmt);\n\treturn ret;\n }\n" + end + struc="" + istruc="" + if stmt.ncols > 0 + if pnum<=1 + $cqgen_simple_fns["#{name}_test"]=1 + end + fbody+=" ret = SQLPrepare(hstmt, \"#{sql}\", SQL_NTS);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody+=" ret = SQLExecute(hstmt);\n" + fbody+=" return ret;\n}\n\n" + struc+="typedef struct {\n" + fbody2+=" hstmt = istmt->hstmt;\n" + fbody2+=" if (result == istmt->result) {\n" + fbody2+="\tgoto doexec;\n }\n" + fbody2+=" istmt->result = NULL;\n" + fbodyt+=" do {\n" + fbodyt+="\tret = #{name}(hstmt, &result);\n" + fbodyt+="\tif (!SQL_SUCCEEDED(ret)) {\n\t break;\n\t}\n" + cols = stmt.columns(1) + coltable = Hash.new + cols.each do |col| + coltable[col.table => 1] + end + cnum=1 + cols.each do |col| + if coltable.size > 1 + cname=col.table+"_"+col.name + else + cname=col.name + end + case col.type + when ODBC::SQL_INTEGER + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLINTEGER #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_LONG," + fbody2+=" &result->#{cname}, sizeof (SQL_C_LONG)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_SMALLINT + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLSMALLINT #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_SMALLINT," + fbody2+=" &result->#{cname}, sizeof (SQL_C_SMALLINT)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_FLOAT + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLDOUBLE #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_DOUBLE," + fbody2+=" &result->#{cname}, sizeof (SQLDOUBLE)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_DOUBLE + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLDOUBLE #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_DOUBLE," + fbody2+=" &result->#{cname}, sizeof (SQL_C_DOUBLE)," + fbody2+=" &result->#{cname}_len);\n" + else + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLCHAR #{cname}[#{col.length}];\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_CHAR," + fbody2+=" result->#{cname}, #{col.length}," + fbody2+=" &result->#{cname}_len);\n" + end + fbody2+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + cnum+=1; + end + sname=$cqgen_stypes[istruc] + if sname.nil? + sname="RESULT_#{name}" + $cqgen_stypes[istruc]=sname + addstruct=true + else + addstruct=false + end + struc+="#{istruc}} #{sname};\n\n" + flvarst+=" #{sname} result;\n\n" + if addstruct + fhead2+=", #{sname} *result" + else + fbody2="" + flvars2="" + fbody3="" + flvars3="" + fname=sname.gsub(/^RESULT_/, "") + fhead2="#define\t\t#{name}\t#{fname}\n" + fhead3="#define\t\t#{name}_deinit\t#{fname}_deinit\n" + end + cnum=1 + cols.each do |col| + if coltable.size > 1 + cname=col.table+"_"+col.name + else + cname=col.name + end + case col.type + when ODBC::SQL_INTEGER + fmt='%ld' + when ODBC::SQL_SMALLINT + fmt='%d' + when ODBC::SQL_FLOAT + fmt='%g' + when ODBC::SQL_DOUBLE + fmt='%g' + else + fmt='%s' + end + fbodyt+="\tif (result.#{cname}_len != SQL_NULL_DATA) {\n" + fbodyt+="\t printf(\"#{sname}.#{cname}=#{fmt}\\n\",\n" + fbodyt+="\t\t result.#{cname});\n" + fbodyt+="\t} else {\n" + fbodyt+="\t printf(\"#{sname}.#{cname}=NULL\\n\");\n" + fbodyt+="\t}\n" + cnum+=1; + end + fbodyt+=" } while (SQL_SUCCEEDED(ret));\n" + fbodyt+=" #{name}_deinit(hstmt);\n" + else + fhead+=", SQLINTEGER *rowCountRet" + fbody=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + fbody + fbody=" ret = SQLAllocStmt(hdbc, &hstmt);\n" + fbody + fbody=" *rowCountRet = 0;\n" + fbody + fbody+=" ret = SQLExecDirect(hstmt, " + fbody+='"'+sql+'"' + fbody+=", SQL_NTS);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + fbody+=" ret = SQLRowCount(hstmt, rowCountRet);\ndone:\n" + fbody+=" SQLFreeStmt(hstmt, SQL_DROP);\n return ret;\n}\n\n" + flvarst+=" SQLINTEGER rowCount;\n\n" + fbodyt+=" printf(\"#{name}_test rowCount=%ld\\n\", rowCount);\n" + end + if fbodyt.length > 0 + fbodyt+=" return ret;\n" + fbodyt+="}\n" + end + fhead+=")\n" + if fbody2.length > 0 + fhead2+=")\n" + fbody2+="doexec:\n" + fbody2+=" ret = SQLFetch(hstmt);\n" + fbody2+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody2+=" istmt->result = result;\n" + fbody2+=" return ret;\n}\n\n" + end + flvars+="\n" + stmt.drop + conn.disconnect + sql = sql.gsub(/[ \t]+/, " ") + sql = sql.gsub(/\/\*/, "") + sql = sql.gsub(/\*\//, "") + $cqgen_h1+="/* #{sql} */\n" if addstruct + $cqgen_h1+=struc if addstruct + $cqgen_h2+="/* #{sql} */\n" + tmp=fhead.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + if fhead2.length > 0 + tmp=fhead2.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + end + if fhead3.length > 0 + tmp=fhead3.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + end + $cqgen_h2+="\n" + $cqgen_c+=fhead + $cqgen_c+=flvars + $cqgen_c+=fbody + $cqgen_c+=fhead2 unless fhead2 =~ /^#define/ + $cqgen_c+=flvars2 + $cqgen_c+=fbody2 + $cqgen_c+=fhead3 unless fhead3 =~ /^#define/ + $cqgen_c+=flvars3 + $cqgen_c+=fbody3 + $cqgen_ct1+="#if defined(TEST_#{name}) || defined(TEST_cqgen_all)\n" + $cqgen_ct1+=fheadt + $cqgen_ct1+=flvarst + $cqgen_ct1+=fbodyt + $cqgen_ct1+="#endif\n\n" +end + +def cqgen_test + $cggen_dsn="" if $cqgen_dsn.nil? + $cggen_uid="" if $cqgen_uid.nil? + $cggen_pwd="" if $cqgen_pwd.nil? + test_calls="" + $cqgen_simple_fns.each_key do |name| + test_calls+=" printf(\"=== #{name} ===\\n\");\n" + test_calls+=" #{name}(hdbc);\n" + end + $cqgen_ct2=$cqgen_ct1 + $cqgen_ct2+=%Q{ +#ifdef TEST_cqgen_all +int +main(int argc, char **argv) +{ + SQLHENV henv; + SQLHDBC hdbc; + SQLRETURN ret; + + ret = SQLAllocEnv(&henv); + if (!SQL_SUCCEEDED(ret)) { + exit(1); + } + ret = SQLAllocConnect(henv, &hdbc); + if (!SQL_SUCCEEDED(ret)) { + exit(2); + } + ret = SQLConnect(hdbc, + \"#{$cqgen_dsn}\", SQL_NTS, + \"#{$cqgen_uid}\", SQL_NTS, + \"#{$cqgen_pwd}\", SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + exit(3); + } + + /* ADD YOUR TEST CALLS HERE */ +#{test_calls} + SQLDisconnect(hdbc); + SQLFreeConnect(hdbc); + SQLFreeEnv(henv); + return 0; +} +#endif + +} +end + +def cqgen_output(c_name=nil, h_name=nil) + h='' + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n\n" + h+=$cqgen_h1 + h+="\n" + h+=$cqgen_h2 + if c_name.nil? + cf=$stdout + ctoclose=false + else + cf=File::open(c_name, 'w') + ctoclose=true + end + if h_name.nil? + hf=cf + htoclose=false + else + hf=File::open(h_name, 'w') + htoclose=true + hb=File::basename(h_name).gsub(/\W/, "_") + h="#ifndef _#{hb}\n#define _#{hb}\n\n#{h}\n#endif /* _#{hb} */\n" + end + hf.print(h) + c='' + if !h_name.nil? && (hf != cf) + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n\n" + c+="#include \"#{h_name}\"\n\n" + end + c+=%Q{ +typedef struct { + SQLHSTMT hstmt; + void *result; +} INTERNAL_STMT; + + +} + c+=$cqgen_c + c+=$cqgen_ct2 + cf.print(c) + hf.close if htoclose + cf.close if ctoclose + $cqgen_h1='' + $cqgen_h2='' + $cqgen_c='' + $cqgen_ct1='' + $cqgen_ct2='' + $cqgen_simple_fns.clear + $cqgen_stypes.clear +end + +# Run this code only when the file is the main program +if $0 == __FILE__ + files=ARGV.flatten + ARGV.clear + files.each do |f| + begin + load f + rescue Exception => err + $stderr.puts("in file #{f}:\n") + $stderr.puts("\t#{err}\n") + end + end +end diff --git a/ruby-odbc-0.999991/COPYING b/ruby-odbc-0.999991/COPYING new file mode 100644 index 0000000..30898e1 --- /dev/null +++ b/ruby-odbc-0.999991/COPYING @@ -0,0 +1,53 @@ +Ruby/ODBC is copyrighted free software by Christian Werner chw at ch-werner.de +You can redistribute it and/or modify it under either the terms of the GPL +(see the file GPL), or the conditions below: + + 1. You may make and give away verbatim copies of the source form of the + software without restriction, provided that you duplicate all of the + original copyright notices and associated disclaimers. + + 2. You may modify your copy of the software in any way, provided that + you do at least ONE of the following: + + a) place your modifications in the Public Domain or otherwise + make them Freely Available, such as by posting said + modifications to Usenet or an equivalent medium, or by allowing + the author to include your modifications in the software. + + b) use the modified software only within your corporation or + organization. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 3. You may distribute the software in object code or binary form, + provided that you do at least ONE of the following: + + a) distribute the binaries and library files of the software, + together with instructions (in the manual page or equivalent) + on where to get the original distribution. + + b) accompany the distribution with the machine-readable source of + the software. + + c) give non-standard binaries non-standard names, with + instructions on where to get the original software distribution. + + d) make other distribution arrangements with the author. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. + diff --git a/ruby-odbc-0.999991/ChangeLog b/ruby-odbc-0.999991/ChangeLog new file mode 100644 index 0000000..a8cad29 --- /dev/null +++ b/ruby-odbc-0.999991/ChangeLog @@ -0,0 +1,350 @@ +ODBC binding for Ruby +--------------------- + +Sat Dec 26 2020 version 0.999991 released + + * update to compile with newer Ruby releases + * report larger width for SQL_(DECIMAL|NUMERIC) columns + +Wed Feb 28 2018 version 0.99999 released + + * update to compile with newer Ruby releases, thanks + Lars Kanis for patch + * added ODBC::Database.login_timeout to get/set the + SQL_LOGIN_TIMEOUT connection attribute + +Wed Feb 15 2017 version 0.99998 released + + * minor update to compile with Ruby 2.4, thangs to Kevin Deisz + * preset output vars before SQLColAttributes() call + +Wed Apr 15 2015 version 0.99997 released + + * fixed bug (typo) when compiling with Ruby < 2.0 + +Wed Mar 25 2015 version 0.99996 released + + * when built for Ruby >= 2.0 release GVL for potentially + long running ODBC API functions, thanks Matt Conover + for initial implementation + +Wed Mar 13 2013 version 0.99995 released + + * added ODBC::Database.use_sql_column_name= flag to switch + usage of SQLColAttributes(SQL_COLUMN_LABEL) to + SQLColAttributes(SQL_COLUMN_NAME) on per connection basis + +Sat Jan 15 2011 version 0.99994 released + + * fixed column key caching, thanks Sean Noonan for bug report + +Fri Dec 24 2010 version 0.99993 released + + * fixed error reporting in Database.transaction method, + thanks Shane Emmons for bug report and testing + +Thu Sep 16 2010 version 0.99992 released + + * ODBC::Statement.each/each_hash now output arrays when + invoked without block + * column keys in result hashes now are cached/recycled Ruby strings + * added ODBC::Database methods use_time/use_utc to directly + output Ruby Time/Date objects + * added encoding support in the UTF8 variant for Ruby >= 1.9 + * added module constant ODBC::UTF8 to indicate variant of module + * fixes for M$SQL server reporting zero column sizes + and unknown data types for varchar(strmax) columns + * eliminated compiler warnings + * use StringValueCStr instead of STR2CSTR (Ruby >= 1.9.1) + * small change for decision when to use SQLGetData() loop + +Sun Apr 25 2010 version 0.99991 released + + * detect HAVE_LONG_LONG for builds with unixODBC + * UTF-8 coding/decoding fix for range 0x80..0xBF + +Thu Feb 18 2010 version 0.9999 released + + * integrated Bosko Ivanisevic's fixes in ext/utf8/extconf.rb + and test/test.rb and test/utf8/test.rb + +Fri Jan 15 2010 version 0.9998 released + + * documentation update + * reorganized directory layout for making a gem + +Wed May 20 2009 version 0.9997 released + + * added Chad Johnson's FreeTDS/MSSQL patch to prevent from + re-prepare of queries with SQL_VARCHAR parameters + +Mon Feb 02 2009 version 0.9996 released + + * updates for Ruby > 1.8 + * added Stephen Veit's SQL_NO_TOTAL handling to improve + retrieving large SQL_C_BINARY data + * fixes for SQLExecute/SQLExecDirect returning SQL_NO_DATA + +Sat Apr 07 2007 version 0.9995 released + + * init.c: prefer libiodbc.so.2 over libiodbc.so and + libodbc.so.1 over libodbc.so and same logic for + the installer shared library + * no exception thrown on stale HSTMT for ODBC::Statement.nrows + +Mon Dec 25 2006 version 0.9994 released + + * improved doc on ODBC::Statement.*param*, ODBC::Parameter, + and related ODBCProc + * added support for output/input-output parameters in queries, + methods ODBC::Statement.param_iotype/param_output_type/ + param_output_size/param_output_value + +Fri Sep 15 2006 version 0.9993 released + + * more tweaks in extconf.rb for --enable-dlopen'ed utf8 version + thanks Daigo Moriwaki for testing + * eliminated warnings for installer functions + +Mon Sep 11 2006 version 0.9992 released + + * added parameter "column" in ODBC::Database::columns + * intern most strings on module load + * enhanced ODBC::Statement.fetch_hash et.al. to produce + intern'ed strings or fixnums as hash keys as requested + by Neville Burnell + * handle SQL_NO_DATA for chunk-wise SQLGetData() + * determine dynamically which API (UNICODE or ANSI) to + use for ODBC installer functions + * more 64 bit fixes + * added missing SQLEndTran() in init.c + +Sat Aug 05 2006 version 0.9991 released + + * fixed more SQLLEN vs SQLINTEGER issues and 64 bit warnings + * bail out during extconf.rb when sql.h/sqlext.h is not + available as requested by Curt Sampson + * fixed transaction logic ENV vs. DBC and use SQLEndTran() + for ODBCVER >= 0x0300, thanks Adam Harper for patch + * detect SQL parameter strings containing '\0's and use + SQL_C_BINARY when appropriate as parameter types to support + BLOB data, thanks Daigo Moriwaki for report and testing + * don't use dlopen()/GCC weak attributes on MacOSX + +Wed Jun 28 2006 version 0.999 released + + * added ODBC::Column@autoincrement information + * added init.c and extconf.rb logic for resolving iODBC/unixODBC + library dependencies at runtime using GCC weak attribute in + conjunction with dlopen(), this feature can be enabled/disabled + with "ruby extconf.rb --enable-dlopen" + +Sun May 21 2006 version 0.998 released + + * added undocumented module methods ODBC::write_file_dsn and + ODBC::read_file_dsn + * fixes in SQLConfigDataSource/SQLInstallerError detect/usage + * added statement level option handling (SQLSetStmtOption), + ODBC::Database.newstmt, ODBC::Statement.prepare|run as + contributed by Carl Blakeley of OpenLink Software + * eliminated most GCC 4.x warnings + +Sun Dec 25 2005 version 0.997 released + + * 32/64 bit issues as pointed out by Lars Kanis + * ODBC::Database.get_info implementation and constants, idea + and initial coding contributed by Tim Haynes of OpenLink Software + * ODBC::Statement.param_type implemented as work around for + Ingres ODBC driver. Thanks Jon Chambers for idea and testing + +Sun Nov 21 2004 version 0.996 released + + * use SQLInstallerError() for adddsn/confdsn/deldsn if available, + code contributed by Ryszard Niewisiewicz + * retry SQLBindParameter() for SQL_VARCHAR when SQLDescribeParam() + doesn't work (e.g. on MS Jet Engine) + * fixed crashes when SQLBindParameter failed in stmt_exec_int(), + as reported by Ryszard Niewisiewicz + +Tue Sep 07 2004 version 0.995 released + + * fixed another buffer alignment bug fetching doubles on sparc, + thanks Dan Debertin for testing. + +Wed Jul 14 2004 version 0.994 released + + * fixed ODBC::Database.new|connect so that + ODBC::Database.new.drvconnect(string) works + * fixed SQLTCHAR detect for Win32 in extconf.rb, + thanks to Pedro Luis Castedo Cepeda + +Mon Jun 28 2004 version 0.993 released + + * use HAVE_TYPE_SQLTCHAR from extconf.rb for old iODBC versions + * don't rb_define_alloc_func of dsn_new/drv_new for Ruby >= 1.8, + gives stack overflows, thanks to Pedro Luis Castedo Cepeda + for fixing + * fixes for iODBC driver manager concerning SQLFetchScroll + availability (SQL state codes IM001 vs HYC000) + * blob handling fix as suggested by Kaspar Schiess + +Mon Mar 22 2004 version 0.992 released + + * fixed buggy UTF8->Unicode conversion code + * fixed handling of uppercase column names in row hashes + +Fri Dec 26 2003 version 0.991 released + + * use SQLConfigDataSourceW() in utf8 version + * SQLError() is now always called after error/warning from + other ODBC API invokations in order to cleanup error + messages in driver manager/driver + * tweaks for SQLColAttributes() problems as suggested + by Nathaniel Talbott + * modified extconf.rb as suggestedb by + * eliminated use of alloca() in utf8 converters + * reworked to use Ruby 1.8 rb_define_alloc_func() API + +Wed May 21 2003 version 0.99 released + + * no functional changes with respect to 0.98 + but starting with 0.99 released under Ruby style license. + +Thu May 08 2003 version 0.98 released + + * added utf8 version (-DUNICODE, -D_UNICODE for CFLAGS), + which generates odbc_utf.so, for build instructions + see README. Thanks Bosko Ivanisevic + for ideas and testing. + +Fri Apr 25 2003 version 0.97 released + + * align buffers for fetching scalar data (long, double et.al.) + to prevent alignment problems causing bus errors e.g. on sparc, + thanks sakazuki for testing. + +Wed Mar 19 2003 version 0.96 released + + * added fall back to SQLFetch() when SQLFetchScroll(SQL_FETCH_NEXT) + or SQLExtendedFetch(SQL_FETCH_NEXT) report IM001 error, + thanks Kiyonori Takeda for testing. + +Mon May 13 2002 version 0.95 released + + * doc updated + * minor code cleanups + * fixed ODBC::Database.run to properly accept blocks + as reported by Michael Neumann + * added ODBC::Statement.more_results method as + requested by Chris Morris + +Thu Oct 11 2001 version 0.94 released + + * doc updated + * fixed off-by-one bug in do_fetch function + * added some more SQL_* constants + * added ODBC::Database.special_columns method + * added unique flag to ODBC::Database.indexes + +Fri Sep 07 2001 version 0.93 released + + * doc updated + * removed default column name conversion to upper case + as introduced in version 0.91 + * added ignorecase, ignorecase= methods for ODBC::Database + and ODBC::Statement as suggested by + Sean O'Halpin + +Fri Aug 31 2001 version 0.92 released + + * ODBC::Parameter class and ODBC::Statement.make_proc method added + * changes to support libiodbc-2.50 eg on Debian, thanks + Massimiliano Mirra for testing + * fixed typos in constant table (SQL_CP_* symbols, O_CONSTU macro) + * if odbcinst.h not available, make dummy DSN config methods + +Sat Aug 25 2001 version 0.91 released + + * handle non-unique keys in column name/result set hashes + by appending "#column-number" string + * convert column names to uppercase if ODBC driver is + identifier case insensitive as suggested by + Elmar Sonnenschein + * added cursorname method in Statement + * added assignment methods for Environment/Database/Statement + options + * taint strings + * use SQLExecDirect() in Statement.run/do when no parameters in query + +Wed Aug 15 2001 version 0.9 released + + * doc updated + * added alloc/free and ODBC API call tracing + (compile-time, -DTRACING) + * added array result for ODBC::Statement.columns + * statements are now by default dropped before SQLDisconnect + * rewrote resource tracking of environment, database connection + and statement objects due to crashes/hangs on Win32 + * improved constructors for data/time/timestamp objects + * added clone methods for date/time/timestamp objects + +Thu Jul 26 2001 version 0.8 released + + * minor doc update + * added handling of block for ODBC::Statement.columns method + * revised error reporting and SQLRETURN handling with + respect to SQL_SUCCESS_WITH_INFO, introduced info method + to get warnings of last ODBC API call. Change was required + to support MS SQL Server, thanks to correspondence with + David Moore . + +Wed Jun 20 2001 version 0.7 released + + * minor doc update + * some more test cases in fetching data + * added some more fetch methods (fetch_many, fetch_all ...) + * added more tests for conditional include of + * again, fixes in parameter binding thanks to patches from + Steve Tuckner + +Tue Jun 12 2001 version 0.6 released + + * reviewed parameter and column information handling + * work around cases when SQLDescribeParam is not supported + by the selected ODBC driver + * bug fixes in parameter binding + +Sun Jun 10 2001 version 0.5 released + + * doc updated + * added environment attribute setting/getting methods + * eliminated alloca() in fetching data + * added missing SQL_CHAR constant + * fixed accessor methods for ODBC::(DSN|Driver|Column) as + suggested by Michael Neumann + +Fri Jun 08 2001 version 0.4 released + + * Cygwin fixes thanks to Michael Neumann + * minor optimizations + * cleanup GC related functions + +Sun May 27 2001 version 0.3 released + + * doc updated + * win32 port (mswin32 working, mingw untested) + * added types (ODBC::Date, ODBC::Time, ODBC::TimeStamp) + * reworked module/class organization + * major review + +Thu May 17 2001 version 0.2 released + + * updated and improved doc + * added fetch_hash, each_hash, column ODBCStmt methods + * added more constants in ODBC class + * added ODBCColumn class + * added accessor for ODBC::error + +Sun May 13 2001 version 0.1 released + + * initial release. diff --git a/ruby-odbc-0.999991/GPL b/ruby-odbc-0.999991/GPL new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/ruby-odbc-0.999991/GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ruby-odbc-0.999991/MANIFEST b/ruby-odbc-0.999991/MANIFEST new file mode 100644 index 0000000..21b835a --- /dev/null +++ b/ruby-odbc-0.999991/MANIFEST @@ -0,0 +1,23 @@ +COPYING +ChangeLog +GPL +MANIFEST +README +ruby-odbc.gemspec +ext/extconf.rb +ext/odbc.c +ext/init.c +ext/utf8/extconf.rb +ext/utf8/odbc.c +ext/utf8/init.c +lib/cqgen.rb +doc/odbc.html +test/test.rb +test/00connect.rb +test/10create_table.rb +test/20insert.rb +test/30select.rb +test/40update.rb +test/50drop_table.rb +test/70close.rb +test/utf8/test.rb diff --git a/ruby-odbc-0.999991/README b/ruby-odbc-0.999991/README new file mode 100644 index 0000000..3a500f9 --- /dev/null +++ b/ruby-odbc-0.999991/README @@ -0,0 +1,114 @@ +# $Id: README,v 1.47 2020/12/25 21:47:43 chw Exp chw $ + +ruby-odbc-0.999991 + +This is an ODBC binding for Ruby. So far it has been tested with + + - Ruby 1.[6-9], MySQL 3.22/MyODBC (local), unixODBC 2.1.0 + on Linux 2.2-x86 and 2.6-x86_64 + + - Ruby 1.6.4, MySQL 3.22/MyODBC (local), libiodbc 2.50 + on Linux 2.2-x86 + + - Ruby 1.[6-8], MySQL 3.22/MyODBC (remote), MS Jet Engine, MSVC++ 6.0 + on Windows NT4SP6 + + - Ruby 1.6.[3-5], MySQL 3.22/MyODBC (remote), MS Jet Engine, cygwin, + on Windows NT4SP6 and 2000 + + - Ruby 1.8.*, SQLite/ODBC >= 0.67, libiodbc 3.52.4 on Fedora Core 3 x86 + + - Ruby 2.0.0, SQLite/ODBC >= 0.93, unixODBC 2.2.14 on Ubuntu 12.04 x86 + + - Ruby >= 2.4 + + Michael Neumann and + Will Merrell reported successful + compilation with Cygwin on Win32. + +Requirements: + + - Ruby 1.6.[3-8] or Ruby >= 1.7 + - unixODBC 2.x or libiodbc 3.52 on UN*X + +Installation: + + $ ruby -Cext extconf.rb [--enable-dlopen|--disable-dlopen] + $ make -C ext + # make -C ext install + + --enable/disble-dlopen turns on/off special initialization + code to make ruby-odbc agnostic to unixODBC/iODBC driver + manager shared library names when GCC is used for compile. + In cases where unixODBC or iODBC is installed in non-standard + locations, use the option --with-odbc-dir= + when running extconf.rb + +Installation of utf8 version: + + $ ruby -Cext/utf8 extconf.rb [--enable-dlopen|--disable-dlopen] + $ make -C ext/utf8 + # make -C ext/utf8 install + +Installation MSVC: + + C:..>ruby -Cext extconf.rb + C:..>cd ext + C:..>nmake + C:..>nmake install + C:..>ruby -Cutf8 extconf.rb + C:..>cd utf8 + C:..>nmake + C:..>nmake install + +Testing: + + $ ruby -Ctest test.rb DSN [uid] [pwd] + or + $ ruby -KU -Ctest/utf8 test.rb DSN [uid] [pwd] + +Usage: + + Refer to doc/odbc.html + + The difference between utf8 and non-utf8 versions are: + + - non-utf8 version uses normal SQL.* ANSI functions + - utf8 version uses SQL.*W UNICODE functions and + requires/returns all strings in UTF8 format + + Thus, depending on the -K option of ruby one could use + that code snippet: + + ... + if $KCODE == "UTF8" then + require 'odbc_utf8' + else + require 'odbc' + fi + + It is also possible to load both non-utf8 and utf8 version + into ruby: + + ... + # non-utf8 version + require 'odbc' + # utf8 version + require 'odbc_utf8' + + Whichever is loaded first, gets the module name 'ODBC'. + The second loaded module will be named 'ODBC_UTF8' (for + 'odbc_utf8') or 'ODBC_NONE' (for 'odbc'). That should + allow to use both versions simultaneously in special + situations. + +TODO: + + - heavier testing + - improve documentation + +Author: + + Christian Werner + mailto:chw @nospam@ ch-werner.de + http://www.ch-werner.de/rubyodbc diff --git a/ruby-odbc-0.999991/doc/odbc.html b/ruby-odbc-0.999991/doc/odbc.html new file mode 100644 index 0000000..d66f349 --- /dev/null +++ b/ruby-odbc-0.999991/doc/odbc.html @@ -0,0 +1,1353 @@ + + + + + + + + + Ruby ODBC Reference + + +

Ruby ODBC Reference

+
+ Last update: Wed, 28 February 2018 +
+
+
+

ODBC

+

+ The module to encapsulate the Ruby ODBC binding. +

+

module functions:

+
+
datasources +
Returns an array of ODBC::DSNs, i.e. all + known data source names. +
drivers +
Returns an array of ODBC::Drivers, + i.e. all known ODBC drivers. +
error +
Returns the last error messages (String array) or nil. + The layout of the warning/error messages + is described here. + Retrieving this message as well as subsequent succeeding ODBC + method invocations do not clear the message. Use the + clear_error method + for that purpose. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + The layout of the warning/error messages + is described here. + Retrieving this message as well as subsequent succeeding ODBC + method invocations do not clear the message. Use the + clear_error method + for that purpose. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) +
Raises an ODBC::Error exception with + String error message value. +
newenv +
Returns a new ODBC::Environment. +
+ connection_pooling[=value] +
Gets or sets the process-wide connection pooling attribute. +
+ to_time(timestamp) +
+ to_time(date,[time]) +
+ to_time(time,[date]) +
Creates a Time object from the specified arguments, + which must be ODBC::Date, + ODBC::Time, or + ODBC::TimeStamp objects. +
+ to_date(timestamp) +
to_date(date) +
Creates a Date object from the specified arguments, + which must be ODBC::Date, or + ODBC::TimeStamp objects. +
connect(dsn,[user,passwd]) + [{|dbc| block}] +
If no block is specified, a connection to the given data source + is established and a ODBC::Database + object is returned, identifying that connection. Otherwise, + the block is executed with the database object. When the block + is finished, the connection is automatically released. + Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
+

constants:

+

+ The boolean constant UTF8 reports the character encoding + of the module. If it is true, the UTF-8 variant of + the module is in use, and string data is automatically converted + to/from Unicode. +

+

+ Some constants of the ODBC API are defined in order to set connection + options, to deal with SQL data types, and to obtain database meta data. +

+
+
Cursor behaviour: +
SQL_CURSOR_FORWARD_ONLY, + SQL_CURSOR_KEYSET_DRIVEN, + SQL_CURSOR_DYNAMIC, + SQL_CURSOR_STATIC +
Concurrency (transactions): +
SQL_CONCUR_READ_ONLY, + SQL_CONCUR_LOCK, + SQL_CONCUR_ROWVER, + SQL_CONCUR_VALUES +
Fetch direction: +
SQL_FETCH_NEXT, + SQL_FETCH_FIRST, + SQL_FETCH_LAST, + SQL_FETCH_PRIOR, + SQL_FETCH_ABSOLUTE, + SQL_FETCH_RELATIVE +
Data types: +
SQL_UNKNOWN_TYPE, + SQL_CHAR, + SQL_NUMERIC, + SQL_DECIMAL, + SQL_INTEGER, + SQL_SMALLINT, + SQL_FLOAT, + SQL_REAL, + SQL_DOUBLE, + SQL_VARCHAR, + SQL_DATETIME, + SQL_DATE, + SQL_TYPE_DATE, + SQL_TIME, + SQL_TYPE_TIME, + SQL_TIMESTAMP, + SQL_TYPE_TIMESTAMP, + SQL_LONGVARCHAR, + SQL_BINARY, + SQL_VARBINARY, + SQL_LONGVARBINARY, + SQL_BIGINT, + SQL_TINYINT, + SQL_BIT, + SQL_GUID +
Parameter related: +
SQL_PARAM_TYPE_UNKNOWN, + SQL_PARAM_INPUT, + SQL_PARAM_OUTPUT, + SQL_PARAM_INPUT_OUTPUT, + SQL_DEFAULT_PARAM + SQL_RETURN_VALUE +
Procedure related: +
SQL_RESULT_COL, + SQL_PT_UNKNOWN, + SQL_PT_PROCEDURE, + SQL_PT_FUNCTION +
Environment attributes: +
SQL_CP_OFF, + SQL_CP_ONE_PER_DRIVER, + SQL_CP_ONE_PER_HENV, + SQL_CP_DEFAULT, + SQL_CP_STRICT_MATCH, + SQL_CP_RELAXED_MATCH, + SQL_CP_MATCH_DEFAULT, + SQL_OV_ODBC2, + SQL_OV_ODBC3 +
Info types for + ODBC::Database.get_info + yielding integer results: +
SQL_ACTIVE_ENVIRONMENTS, + SQL_ACTIVE_CONNECTIONS, + SQL_ACTIVE_STATEMENTS, + SQL_ASYNC_MODE, + SQL_CATALOG_LOCATION, + SQL_CONCAT_NULL_BEHAVIOR, + SQL_CORRELATION_NAME, + SQL_CURSOR_COMMIT_BEHAVIOR, + SQL_CURSOR_ROLLBACK_BEHAVIOR, + SQL_CURSOR_SENSITIVITY, + SQL_DDL_INDEX, + SQL_DEFAULT_TXN_ISOLATION, + SQL_DRIVER_HDBC, + SQL_DRIVER_HENV, + SQL_DRIVER_HDESC, + SQL_DRIVER_HLIB, + SQL_DRIVER_HSTMT, + SQL_FILE_USAGE, + SQL_GROUP_BY, + SQL_IDENTIFIER_CASE, + SQL_MAX_ASYNC_CONCURRENT_STATEMENTS, + SQL_MAX_BINARY_LITERAL_LEN, + SQL_MAX_CATALOG_NAME_LEN, + SQL_MAX_CHAR_LITERAL_LEN, + SQL_MAX_COLUMN_NAME_LEN, + SQL_MAX_COLUMNS_IN_GROUP_BY, + SQL_MAX_COLUMNS_IN_INDEX, + SQL_MAX_COLUMNS_IN_ORDER_BY, + SQL_MAX_COLUMNS_IN_SELECT, + SQL_MAX_COLUMNS_IN_TABLE, + SQL_MAX_CONCURRENT_ACTIVITIES, + SQL_MAX_CURSOR_NAME_LEN, + SQL_MAX_DRIVER_CONNECTIONS, + SQL_MAX_IDENTIFIER_LEN, + SQL_MAX_INDEX_SIZE, + SQL_MAX_OWNER_NAME_LEN, + SQL_MAX_PROCEDURE_NAME_LEN, + SQL_MAX_QUALIFIER_NAME_LEN, + SQL_MAX_ROW_SIZE, + SQL_MAX_SCHEMA_NAME_LEN, + SQL_MAX_STATEMENT_LEN, + SQL_MAX_TABLE_NAME_LEN, + SQL_MAX_TABLES_IN_SELECT, + SQL_MAX_USER_NAME_LEN, + SQL_NON_NULLABLE_COLUMNS, + SQL_NULL_COLLATION, + SQL_ODBC_API_CONFORMANCE, + SQL_ODBC_INTERFACE_CONFORMANCE, + SQL_ODBC_SAG_CLI_CONFORMANCE, + SQL_ODBC_SQL_CONFORMANCE, + SQL_PARAM_ARRAY_ROW_COUNTS, + SQL_PARAM_ARRAY_SELECTS, + SQL_QUALIFIER_LOCATION, + SQL_QUOTED_IDENTIFIER_CASE, + SQL_SQL_CONFORMANCE, + SQL_TXN_CAPABLE +
Info types for + ODBC::Database.get_info + yielding bitmasks (integer results): +
SQL_AGGREGATE_FUNCTIONS, + SQL_ALTER_DOMAIN, + SQL_ALTER_TABLE, + SQL_BATCH_ROW_COUNT, + SQL_BATCH_SUPPORT, + SQL_BOOKMARK_PERSISTENCE, + SQL_CATALOG_USAGE, + SQL_CONVERT_BINARY, + SQL_CONVERT_BIT, + SQL_CONVERT_CHAR, + SQL_CONVERT_GUID, + SQL_CONVERT_DATE, + SQL_CONVERT_DECIMAL, + SQL_CONVERT_DOUBLE, + SQL_CONVERT_FLOAT, + SQL_CONVERT_FUNCTIONS, + SQL_CONVERT_INTEGER, + SQL_CONVERT_INTERVAL_YEAR_MONTH, + SQL_CONVERT_INTERVAL_DAY_TIME, + SQL_CONVERT_LONGVARBINARY, + SQL_CONVERT_LONGVARCHAR, + SQL_CONVERT_NUMERIC, + SQL_CONVERT_REAL, + SQL_CONVERT_SMALLINT, + SQL_CONVERT_TIME, + SQL_CONVERT_TIMESTAMP, + SQL_CONVERT_TINYINT, + SQL_CONVERT_VARBINARY, + SQL_CONVERT_VARCHAR, + SQL_CONVERT_WCHAR, + SQL_CONVERT_WLONGVARCHAR, + SQL_CONVERT_WVARCHAR, + SQL_CREATE_ASSERTION, + SQL_CREATE_CHARACTER_SET, + SQL_CREATE_COLLATION, + SQL_CREATE_DOMAIN, + SQL_CREATE_SCHEMA, + SQL_CREATE_TABLE, + SQL_CREATE_TRANSLATION, + SQL_CREATE_VIEW, + SQL_DATETIME_LITERALS, + SQL_DROP_ASSERTION, + SQL_DROP_CHARACTER_SET, + SQL_DROP_COLLATION, + SQL_DROP_DOMAIN, + SQL_DROP_SCHEMA, + SQL_DROP_TABLE, + SQL_DROP_TRANSLATION, + SQL_DROP_VIEW, + SQL_DTC_TRANSITION_COST, + SQL_DYNAMIC_CURSOR_ATTRIBUTES1, + SQL_DYNAMIC_CURSOR_ATTRIBUTES2, + SQL_FETCH_DIRECTION, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2, + SQL_GETDATA_EXTENSIONS, + SQL_KEYSET_CURSOR_ATTRIBUTES1, + SQL_KEYSET_CURSOR_ATTRIBUTES2, + SQL_INDEX_KEYWORDS, + SQL_INFO_SCHEMA_VIEWS, + SQL_INSERT_STATEMENT, + SQL_LOCK_TYPES, + SQL_NUMERIC_FUNCTIONS, + SQL_OJ_CAPABILITIES, + SQL_OWNER_USAGE, + SQL_POS_OPERATIONS, + SQL_POSITIONED_STATEMENTS, + SQL_QUALIFIER_USAGE, + SQL_SCHEMA_USAGE, + SQL_SCROLL_CONCURRENCY, + SQL_SCROLL_OPTIONS, + SQL_SQL92_DATETIME_FUNCTIONS, + SQL_SQL92_FOREIGN_KEY_DELETE_RULE, + SQL_SQL92_FOREIGN_KEY_UPDATE_RULE, + SQL_SQL92_GRANT, + SQL_SQL92_NUMERIC_VALUE_FUNCTIONS, + SQL_SQL92_PREDICATES, + SQL_SQL92_RELATIONAL_JOIN_OPERATORS, + SQL_SQL92_REVOKE, + SQL_SQL92_ROW_VALUE_CONSTRUCTOR, + SQL_SQL92_STRING_FUNCTIONS, + SQL_SQL92_VALUE_EXPRESSIONS, + SQL_STANDARD_CLI_CONFORMANCE, + SQL_STATIC_CURSOR_ATTRIBUTES1, + SQL_STATIC_CURSOR_ATTRIBUTES2, + SQL_STATIC_SENSITIVITY, + SQL_STRING_FUNCTIONS, + SQL_SUBQUERIES, + SQL_SYSTEM_FUNCTIONS, + SQL_TIMEDATE_ADD_INTERVALS, + SQL_TIMEDATE_DIFF_INTERVALS, + SQL_TIMEDATE_FUNCTIONS, + SQL_TXN_ISOLATION_OPTION, + SQL_UNION +
Info types for + ODBC::Database.get_info + yielding strings: +
SQL_ACCESSIBLE_PROCEDURES, + SQL_ACCESSIBLE_TABLES, + SQL_CATALOG_NAME, + SQL_CATALOG_NAME_SEPARATOR, + SQL_CATALOG_TERM, + SQL_COLLATION_SEQ, + SQL_COLUMN_ALIAS, + SQL_DATA_SOURCE_NAME, + SQL_DATA_SOURCE_READ_ONLY, + SQL_DATABASE_NAME, + SQL_DBMS_NAME, + SQL_DBMS_VER, + SQL_DESCRIBE_PARAMETER, + SQL_DM_VER, + SQL_DRIVER_NAME, + SQL_DRIVER_ODBC_VER, + SQL_DRIVER_VER, + SQL_EXPRESSIONS_IN_ORDERBY, + SQL_IDENTIFIER_QUOTE_CHAR, + SQL_INTEGRITY, + SQL_KEYWORDS, + SQL_LIKE_ESCAPE_CLAUSE, + SQL_MAX_ROW_SIZE_INCLUDES_LONG, + SQL_MULT_RESULT_SETS, + SQL_MULTIPLE_ACTIVE_TXN, + SQL_NEED_LONG_DATA_LEN, + SQL_ODBC_SQL_OPT_IEF, + SQL_ODBC_VER, + SQL_ORDER_BY_COLUMNS_IN_SELECT, + SQL_OUTER_JOINS, + SQL_OWNER_TERM, + SQL_PROCEDURE_TERM, + SQL_PROCEDURES, + SQL_QUALIFIER_NAME_SEPARATOR, + SQL_QUALIFIER_TERM, + SQL_ROW_UPDATES, + SQL_SCHEMA_TERM, + SQL_SEARCH_PATTERN_ESCAPE, + SQL_SERVER_NAME, + SQL_SPECIAL_CHARACTERS, + SQL_TABLE_TERM, + SQL_USER_NAME, + SQL_XOPEN_CLI_YEAR +
Options for + ODBC::Database.get_option, + ODBC::Database.set_option, + ODBC::Statement.get_option, + and + ODBC::Statement.set_option + yielding integers: +
SQL_AUTOCOMMIT, + SQL_LOGIN_TIMEOUT, + SQL_CONCURRENCY, + SQL_QUERY_TIMEOUT, + SQL_MAX_ROWS, + SQL_MAX_LENGTH, + SQL_NOSCAN, + SQL_ROWSET_SIZE, + SQL_CURSOR_TYPE +
+
+
+
+

ODBC::Object

+

+ The class to represent the root of all other ODBC related objects. +

+

super class:

+ Object +

methods:

+
+
error +
Returns the last error message (String) or nil. For further + information see ODBC::error. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + For further information see + ODBC::error. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) + +
Raises an ODBC::Error exception with + String error message value. +
+

singleton methods:

+
+
error +
Returns the last error message (String array) or nil. + For further information see + ODBC::error. +
info +
Returns the last driver/driver manager warning messages + (String array) or nil. + For further information see + ODBC::error. +
clear_error +
Resets the last driver/driver manager error and warning messages + to nil. +
raise(value) + +
Raises an ODBC::Error exception with + String error message value. +
+
+
+
+

ODBC::Environment

+

+ The class to represent the environment for + ODBC::Databases. +

+

super class:

+ ODBC::Object +

methods:

+
+
connect(dsn,[user,passwd]) +
Connects to an ODBC data source and returns a + ODBC::Database object. + Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
environment + +
Returns the ODBC::Environment + of the object. +
commit +
Commits the current transaction. +
rollback +
Rollbacks the current transaction. +
+ transaction {|env| block} +
First commits the current transaction, then executes the given + block where the paramater is the object itself (an environment or + a database connection). If the block raises an exception, the + transaction is rolled back, otherwise committed. +
+ connection_pooling[=value] +
Gets or sets the connection pooling attribute of the environment. +
+ cp_match[=value] +
Gets or sets the connection pooling match attribute of the + environment. +
+ odbc_version[=value] +
Gets or sets the ODBC version attribute of the environment. +
+

singleton methods:

+
+
new +
Returns a new ODBC::Environment + object. +
+
+
+
+

ODBC::Database

+

+ The class to represent a connection to an ODBC data source. +

+

+ When underlying ODBC SQL functions report an error, + an ODBC::Error + exception with a corresponding error message from the ODBC + driver manager and/or driver is raised. +

+

super class:

+ ODBC::Environment +

methods:

+
+
connected? +
Returns true when the object is in connected + state, false otherwise. +
drvconnect(drv) +
Connect to a data source specified by drv + (ODBC::Driver). +
+ disconnect(no_drop=false) +
Disconnects from the data source, all active statements for the + connection are dropped except when the no_drop argument + is true. The method returns true when the connection was released, + false otherwise. +
newstmt +
Returns a new ODBC::Statement + object without preparing or executing a SQL statement. This allows + to use + ODBC::Statement.set_option + to set special options on the statement before actual execution. +
tables([pattern]) +
Returns an ODBC::Statement with + information of all or some (pattern String given) + tables of the data source. +
columns([table[,column]]) +
Returns an ODBC::Statement + with column information + of the given table (String) and optional + column (String). +
+ indexes([table[,unique]]) +
Returns an ODBC::Statement + with index information + of the given table (String). If unique + is given and true, information for unique indexes only is + returned. +
types([tcode]) +
Returns an ODBC::Statement + with type information + of the numeric SQL type tcode or all types if + tcode is omitted. +
+ primary_keys([table]) +
Returns an ODBC::Statement + with primary key information of the given table (String). +
+ foreign_keys([table]) +
Returns an ODBC::Statement + with foreign key information of the given table (String). +
+ table_privileges([table]) +
Returns an ODBC::Statement + with owner/right information of the given table (String). +
procedures([p]) +
Returns an ODBC::Statement + with information about stored procedures. +
+ procedure_columns([p]) +
Returns an ODBC::Statement + with information about stored procedures. +
+ special_columns([table[,id,scope]]) +
Returns an ODBC::Statement + with column information + of the given table (String) indicating the + optimal unique or identifying columns. + id may + be specified as ODBC::SQL_BEST_ROWID or + ODBC::SQL_ROW_VER. scope may be + specified as ODBC::SQL_SCOPE_CURROW, + ODBC::SQL_SCOPE_TRANSACTION, or + ODBC::SQL_SCOPE_SESSION. If omitted the + defaults are ODBC::SQL_BEST_ROWID and + ODBC::SQL_SCOPE_CURROW. +
+ get_info(info_type[,sql_type]) +
Retrieves database meta data according to info_type + and returns numbers or strings. info_type may be specified + as integer or string. To force interpretation of the return value + of the underlying ODBC function SQLGetInfo() as a + specific Ruby type the optional parameter sql_type + can be given as e.g. + ODBC::SQL_SMALLINT (yielding integer), + ODBC::SQL_INTEGER (yielding integer), and + ODBC::SQL_CHAR (yielding string). +
run(sql[,args...]) +
Prepares and executes the query specified by sql + with parameters bound from args and returns an + ODBC::Statement or nil, if + the query did not produce a result set, e.g. when executing + SQL insert, delete, or update statements. +
do(sql[,args...]) +
Prepares and executes the query as in + run, + but returns the number of result rows and automatically drops + the statement. Useful for SQL insert, delete, or update statements. +
prepare(sql) +
Prepares the query specified by sql and returns + an ODBC::Statement. +
proc(sql,[type,size[,n=1]]) + {|stmt| block} +
Prepares the query specified by sql within a + ODBCProc and returns that procedure. + When the procedure is called, the statement is executed with + the procedure's arguments bound to the statement's parameters + and the statement is bound as parameter in the block, e.g. +
# add customer given name and id
+# and return number of affected rows
+
+addcust = dbc.proc("insert into customer values(?, ?)") { |stmt|
+    stmt.nrows
+}
+
+addcust.call("J.R. User", 9999)
+
+# alternative to call: addcust["J.R. User", 9999]
+ The optional arguments type, size, and + n, + make the n-th statement parameter into an output parameter + with that given ODBC data type and size. That statement + parameter must be omitted when the ODBCProc + is called. This can be useful for wrapping stored procedures, e.g. +
# wrap statement with output parameter
+
+aproc = dbc.proc("{ ? = call stored_proc(?) }", ODBC::SQL_INTEGER, 4) {}
+
+# call this statement/procedure, output parameter is result
+
+result = aproc.call(99)
+
+# alternative to call: result = aproc[99]
+ Finalization of an ODBCProc can be + done according to this code snippet +
# aproc is wrapped statement, finalize it now
+
+aproc.statement.drop
+
+ get_option(option) +
Gets a connection level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. This method returns the integer value of that option. +
+ set_option(option,intval) +
Sets a connection level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. The second parameter intval can be used to + set driver-specific statement options. +
autocommit[=bool] +
Sets or queries the autocommit option of the connection. +
+ concurrency[=intval] +
Sets or queries the concurrency mode of cursors opened on + the connection. +
maxrows[=intval] +
Sets or queries the maximum rows returned in result sets from + the connection. +
timeout[=intval] +
Sets or queries the number of seconds to wait for queries to + execute on the connection before returning. +
+ login_timeout[=intval] +
Sets or queries the number of seconds to wait for a login to + complete on the connection before returning. +
maxlength[=intval] +
Sets or queries the maximum amount of data that will be returned + from a character or binary column. +
+ cursortype[=intval] +
Sets or queries the cursor type used for fetches. +
noscan[=bool] +
Sets or queries whether the driver scans SQL strings for ODBC + escape clauses. +
rowsetsize +
Returns the number of rows in a rowset fetched internally by a + call o SQLExtendedFetch (hardcoded to 1). +
ignorecase[=bool] +
Sets or queries the uppercase conversion for column names. If + turned on (bool is true), + ODBC::Statements + created by database methods will report column names in + ODBC::Column + or in the statement fetch methods + as uppercase strings. Otherwise (the default) column names are + passed unmodified. +
drop_all +
Releases the resources of all open + ODBC::Statements in this + database connection. +
use_time[=bool] +
Sets or queries the mapping of SQL_DATE, SQL_TIME, and + SQL_TIMESTAMP data types to Ruby objects. When true, + SQL_DATE is mapped to Ruby Date objects, SQL_TIME and + SQL_TIMESTAMP are mapped to Ruby Time objects. Otherwise (default) + ODBC::Date, + ODBC::Time, and + ODBC::TimeStamp are used. +
use_utc[=bool] +
Sets or queries the timezone applied on SQL_DATE, SQL_TIME, and + SQL_TIMESTAMP data types to Ruby objects. When true, + Ruby Date and Time objects are represented in UTC, when + false (default) in the local timezone. +
+ use_sql_column_name[=bool] +
Sets or queries the flag controlling how column names are + read from the data source. When false (default), the ODBC + API SQLColAttributes(SQL_COLUMN_LABEL) is used, + otherwise SQLColAttributes(SQL_COLUMN_NAME) is used. +
+

singleton methods:

+
+
new +
Creates an unconnected ODBC object, e.g. for a later + drvconnect method call. +
new(dsn,[user,passwd]) +
Connect to an ODBC data source. Options are: +
+
dsn: Data source name (String or + ODBC::DSN) +
user: Login user name (String) +
passwd: Login password (String) +
+
+

Remarks:

+ The run, + prepare, + do, and info methods + (e.g. tables) + can be invoked with a block. In this case the block is executed with + the ODBC::Statement as parameter. The + run and + do methods use the ODBC API + function SQLExecDirect() when the SQL statement has no + parameters. +
+
+
+

ODBC::Statement

+

+ The class to represent a query and its query result. + The object of this class is created as the result of every query. + You may need to invoke the close + or drop methods for the + finished object for better memory performance. +

+

+ When underlying ODBC SQL functions report an error, + an ODBC::Error + exception with a corresponding error message from the ODBC driver + manager and/or driver is raised. +

+

+ ODBC to Ruby type mapping: +

+ + + + + + + + + + + +
ODBCRuby
SQL_SMALLINT, SQL_INTEGER, SQL_TINYINT, SQL_BITT_FIXNUM, T_BIGNUM
SQL_FLOAT, SQL_DOUBLE, SQL_REALT_FLOAT
SQL_DATE, SQL_TYPE_DATEODBC::Date or Date, + see ODBC::Database.use_time +
SQL_TIME, SQL_TYPE_TIMEODBC::Time or Time, + see ODBC::Database.use_time +
SQL_TIMESTAMP, SQL_TYPE_TIMESTAMPODBC::TimeStamp or Time, + see ODBC::Database.use_time +
all othersT_STRING
+

super class:

+

+ ODBC::Database +

+

mixins:

+

+ Enumerable +

+

methods:

+
+
cancel +
Cancel and close the + ODBC::Statement. +
close +
Close the ODBC::Statement. +
drop +
Close and free the ODBC::Statement. +
column(n) +
Returns information of the n-th column of the query result + as ODBC::Column. +
columns(as_ary=false) +
Returns a hash of column information keyed by column names + (Strings) of the query result. If as_ary is true, + an array is returned. In both cases the elements are + ODBC::Columns. If the hash keys + happen to be not unique later columns get their column number + appended, e.g. FOOBAR, FOOBAR#1. +
columns + {|col| block} +
Iterates over the columns of the query result with col + bound to each ODBC::Column. +
ncols +
Returns the number of columns of the query result. +
nrows +
Returns the number of rows of the query result. +
cursorname[=name] +
Returns or sets the cursor name of the statement. +
ignorecase[=bool] +
Same as + ODBC::Database.ignorecase + but affecting this statement only. + Inherited by the current state of the + ODBC::Database at the time the + statement is created. +
fetch +
Returns the next row of the query result as an array. +
fetch_first +
Returns the first row of the query result as an array. +
fetch_scroll(direction, + offset=1) +
Returns the row addressed by direction, e.g. + SQL_FETCH_LAST as an array. offset is + used for SQL_FETCH_RELATIVE and + SQL_FETCH_ABSOLUTE. +
fetch_many(count) +
Returns an array of the next count rows of the query + result, where each row is an array. +
fetch_all +
Same as fetch_many except that all remaining rows + are returned. +
+ fetch_hash(with_table_names=false,use_sym=false) +
Returns the next row of the query result as a hash keyed by + column names. If with_table_names is true, + the keys are combined table/column names. For uniqueness of keys + the same rules as in the columns + method are applied. If use_sym is true, + the keys are formed by intern'ing the (table and) column names. + Alternatively, one hash argument can be presented to + fetch_hash, e.g. + { :key => :Symbol, :table_names => false } or + { :key => :String, :table_names => true } or + { :key => :Fixnum }. +
each {|row| + block} +
Iterates over the query result, performing a fetch + for each row. +
+ each_hash(with_table_names=false,use_sym=false) + {|row| block} +
Iterates over the query result, performing a + fetch_hash for each row. The same + rules for arguments as in fetch_hash apply. +
execute([args...]) +
Binds args to current query and executes it. +
+ run(sql[,args...]) +
Prepares and executes the query specified by sql + with parameters bound from args. +
+ prepare(sql) +
Prepares the query specified by sql. +
more_results +
Returns true and switches over to the next result set, + if the query produced more than one result set. Otherwise + returns false. +
+ get_option(option) +
Gets a statement level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. This method returns the integer value of that option. +
+ set_option(option,intval) +
Sets a statement level option. option can be a + module constant, e.g. SQL_MAX_ROWS, a number, or a + string. The second parameter intval can be used to + set driver-specific statement options. +
+ concurrency[=intval] +
Sets or queries the concurrency mode of cursors opened on + the statement. +
maxrows[=intval] +
Sets or queries the maximum rows returned by the statement. +
timeout[=intval] +
Sets or queries the number of seconds to wait for the statement + to execute before returning. +
+ maxlength[=intval] +
Sets or queries the maximum amount of data that will be returned + from a character or binary column. +
+ cursortype[=intval] +
Sets or queries the cursor type used for fetches. +
noscan[=bool] +
Sets or queries whether the driver scans SQL strings for ODBC + escape clauses. +
rowsetsize +
Returns the number of rows in a rowset fetched internally by + a call to SQLExtendedFetch (hardcoded to 1). +
nparams +
Returns the number of parameters of the statement. +
parameter(n) +
Returns information of the n-th parameter of the query + as ODBC::Parameter. +
parameters +
Returns an array of + ODBC::Parameters describing + all parameters of the query. +
param_type(n[,type,coldef,scale]) +
Allows to override the type of parameter n + and returns the current type. This can be useful + when the ODBC driver does not provide proper type information + on SQLDescribeParam. +
param_iotype(n[,iotype]) +
Allows to change the input/output type of parameter n + and returns the current input/output type. By + default, all parameters are ODBC::SQL_PARAM_INPUT. + When calling a stored procedure in the statement, the output + parameter must be altered using this method, e.g. +
# assume 1st parameter is input, 2nd is output
+stmt = conn.prepare("call stored_proc( ?, ?)")
+# setup 2nd for output of an integer
+stmt.param_iotype(2, ODBC::SQL_PARAM_OUTPUT)
+stmt.param_output_type(2, ODBC::SQL_INTEGER)
+stmt.param_output_size(2, 4)
+# execute stmt and retrieve value of output parameter
+stmt.execute(42, nil)
+stmt.fetch_all
+out_value = stmt.param_output_value(2);
+
param_output_type(n[,type]) +
Allows to change the SQL type of output parameter n + and returns the current type. For further + information see the sample code in the + param_iotype method. +
param_output_size(n[,size]) +
Allows to change the byte size of the buffer for output parameter + n and returns the current size. + For further information see the sample code in the + param_iotype method. +
param_output_value(n) +
Returns the value of the output parameter n. + For further information see the sample code in the + param_iotype method. +
make_proc([n]) +
Wraps the statement into a ODBCProc object + and returns that object. The optional n argument is + the parameter number of the statement which is used as output + parameter in the wrapped ODBCProc. For + further information refer to the samples in the description of + ODBC::Database.proc. +
+

singleton methods:

+
+
make_proc(stmt,[n]) +
Wraps the statement stmt into a + ODBCProc object + and returns that object. The optional n argument is + the parameter number of the statement which is used as output + parameter in the wrapped ODBCProc. For + further information refer to the samples in the description of + ODBC::Database.proc. +
+

Remarks:

+ The fetch, + fetch_hash, + and execute + methods can be invoked with a block. In this case the block is + executed with one row of the result set (fetch and + fetch_hash) or with the + ODBC::Statement (execute) + as parameter. +

If the ignorecase option + is turned on, all column names used in the column, + columns, and *_hash methods are converted to + upper case. +

+
+
+

ODBC::Column

+

+ The class to represent (read-only) information of a column of a query. + Objects of this class are created as result of the + column + and columns methods of + ODBC::Statement. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name +
Returns the column name (String). +
table +
Returns the table name (String). +
length +
Returns the length of the column (Integer). +
nullable +
Returns the nullable state of the column (Boolean). +
searchable +
Returns the searchable state of the column (Boolean). +
unsigned +
Returns the unsigned flag of the column (Boolean). +
precision +
Returns the precision of the column (Integer). +
scale +
Returns the scale of the column (Integer). +
type +
Returns the SQL type of the column (Integer). +
autoincrement +
Returns true if the column automatically increments, + false, if not, and nil if that information cannot be + determined from the column. +
+
+
+
+

ODBC::Parameter

+

+ The class to represent (read-only) information of a parameter of a + query. + Objects of this class are created as result of the + parameter and + parameters methods of + ODBC::Statement. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
type +
Returns the parameter's type, e.g. ODBC::SQL_CHAR. +
precision +
Returns the parameter's precision (Integer). +
length +
Returns the parameter's scale (Integer). +
nullable +
Returns the nullable state of the column (Boolean). +
iotype +
Returns the parameter's input/output type, + e.g. ODBC::SQL_PARAM_INPUT. +
output_type +
Returns the parameter's output type, only useful when the + parameter is an output parameter (ODBC::SQL_PARAM_OUTPUT or + ODBC::SQL_PARAM_INPUT_OUTPUT). +
output_size +
Returns the parameter's output buffer size, only useful when the + parameter is an output parameter (ODBC::SQL_PARAM_OUTPUT). +
+
+
+
+

ODBC::Date

+

+ The class to represent a SQL_DATE column in a table + or a SQL_DATE query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(adate) +
Comparison, compares date with adate and returns + 0, 1, or -1. +
day[=num] +
Returns or sets the day component of the date object. +
month[=num] +
Returns or sets the month component of the date object. +
year[=num] +
Returns or sets the year component of the date object. +
to_s +
Returns a string representation of the object with format + YYYY-MM-DD. +
clone +
Returns a fresh copy of the date object. +
+

singleton methods:

+
+
new([year, month, + day]) +
new(date) +
new(time) +
new(string) +
Creates a new date object from numeric values or from + a Date, Time, or String + object. Recognized string formats are e.g. +
2001-01-01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{d '2001-01-01'}
+
+
+
+
+

ODBC::Time

+

+ The class to represent a SQL_TIME column in a table + or a SQL_TIME query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(atime) +
Comparison, compares time with atime and returns + 0, 1, or -1. +
second[=num] +
Returns or sets the second component of the time object. +
minute[=num] +
Returns or sets the minute component of the time object. +
hour[=num] +
Returns or sets the hour component of the time object. +
to_s +
Returns a string representation of the object with format + hh:mm:ss. +
clone +
Returns a fresh copy of the time object. +
+

singleton methods:

+
+
new([hour, minute, + second]) +
new(time) +
new(string) +
Creates a new time object from numeric values or from + a Time or String object. + Recognized string formats are e.g. +
12:00:01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{t '12:00:01'}
+
+
+
+
+

ODBC::TimeStamp

+

+ The class to represent a SQL_TIMESTAMP column in a table + or a SQL_TIMESTAMP query parameter. +

+

super class:

+

+ ODBC::Object +

+

mixins:

+

+ Comparable +

+

methods:

+
+
<=>(atimestamp) +
Comparison, compares time stamp with atimestamp + and returns 0, 1, or -1. +
fraction[=num] +
Returns or sets the fraction component of the time stamp object. + Note that this is expressed in nanoseconds. +
second[=num] +
Returns or sets the second component of the time stamp object. +
minute[=num] +
Returns or sets the minute component of the time stamp object. +
hour[=num] +
Returns or sets the hour component of the time stamp object. +
day[=num] +
Returns or sets the day component of the time stamp object. +
month[=num] +
Returns or sets the month component of the time stamp object. +
year[=num] +
Returns or sets the year component of the time stamp object. +
to_s +
Returns a string representation of the object with format + YYYY-MM-DD hh:mm:ss fraction. +
clone +
Returns a fresh copy of the time stamp object. +
+

singleton methods:

+
+
new([year, month, + day, hour, minute, + second, fraction]) +
new(time) +
new(string) +
Creates a new time stamp object from numeric values or from + a Time or String object. + Recognized string formats are e.g. +
12:00:01
+2001-01-01
+2001-01-01 12:00:01
+{ts '2001-01-01 12:00:01.1'}
+{d '2001-01-01'}
+{t '12:00:01'}
+
+
+
+
+

ODBC::DSN

+

+ The class to represent a data source name. Objects of this class are + created as result of a + ODBC::datasources + module function call. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name[=name] +
Queries or sets the name (String) of the data source. +
descr[=descr] +
Queries or sets the descr (description, String) + of the data source. +
+

singleton methods:

+
+
new +
Returns an empty ODBC::DSN object. +
+
+
+
+

ODBC::Driver

+

+ The class to represent an ODBC driver with name and attributes. + Objects of this class are + created as result of a + ODBC::drivers + module function call. +

+

super class:

+

+ ODBC::Object +

+

methods:

+
+
name[=name] +
Queries or sets the name (String) of the ODBC driver. +
attrs[[key][=value]] +
Queries or sets attributes in the attrs Hash of the + ODBC driver object. The keys and values + should be Strings. +
+

singleton methods:

+
+
new +
Returns an empty ODBC::Driver object. +
+
+
+
+

ODBCProc

+

+ The class to represent a procedure with ODBC database/statement + context. + Objects of this class are created as result of a + ODBC::Database.proc method call. +

+

super class:

+

+ Proc +

+

methods:

+
+
call([args*]) +
Executes the SQL statement with parameters set from args + and then invokes the procedure's block, setting the block's + single parameter to the + ODBC::Statement. +
[[args*]] +
Synonym for call. +
+
+
+
+

ODBC::Error

+

+ The class to represent ODBC related exceptions. The descriptive + string is made up of the first ODBC driver or driver manager + message as concatenation of SQL state, native error, driver manager + name, database name/vendor, DSN, and error text, e.g.

+S1000 (1146) [unixODBC][TCX][MySQL]Table 'test.foo' doesn't exist
+ For internally generated errors, e.g. method invocation on + a broken connection, the descriptive string starts with + 'INTERN' and native error 0, e.g.
+INTERN (0) [RubyODBC]No connection
+ For errors programmatically generated by the + raise method, + the descriptive string starts with 'INTERN' and native error 1, + e.g.
+INTERN (1) [RubyODBC]Programmer forgot to RTFM
+

+

super class:

+

+ StandardError +

+
+
+
+

Undocumented

+

Use The Source, Luke!

+

+ ODBC::add_dsn(driver,issys=false)
+ ODBC::add_dsn(name,attrs,issys=false)
+ ODBC::config_dsn(driver,issys=false)
+ ODBC::config_dsn(name,attrs,issys=false)
+ ODBC::del_dsn(driver,issys=false)
+ ODBC::del_dsn(name,attrs,issys=false)
+ ODBC::write_file_dsn(filename,appname,key[,value])
+ ODBC::read_file_dsn(filename,appname,key)
+ ODBC::trace([mask])
+
+ ODBC::Statement.fetch!
+ ODBC::Statement.fetch_first!
+ ODBC::Statement.fetch_first_hash(with_table_names=false,use_sym=false)
+ ODBC::Statement.fetch_scroll!(direction,offset=1)
+ ODBC::Statement.fetch_hash!(with_table_names=false,use_sym=false)
+

+
+
+ mailto:Christian Werner +
+ + diff --git a/ruby-odbc-0.999991/ext/extconf.rb b/ruby-odbc-0.999991/ext/extconf.rb new file mode 100644 index 0000000..d252510 --- /dev/null +++ b/ruby-odbc-0.999991/ext/extconf.rb @@ -0,0 +1,157 @@ +require 'mkmf' + +if ! defined? PLATFORM + PLATFORM = RUBY_PLATFORM +end + +def have_library_ex(lib, func="main", headers=nil) + checking_for "#{func}() in -l#{lib}" do + libs = append_library($libs, lib) + if !func.nil? && !func.empty? && COMMON_LIBS.include?(lib) + true + elsif try_func(func, libs, headers) + $libs = libs + true + else + false + end + end +end + +dir_config("odbc") +have_header("version.h") +have_header("ruby/version.h") +have_header("sql.h") || begin + puts "ERROR: sql.h not found" + exit 1 +end +have_header("sqlext.h") || begin + puts "ERROR: sqlext.h not found" + exit 1 +end +testdlopen = enable_config("dlopen", false) +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLTCHAR", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLTCHAR manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLTCHAR" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLLEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLLEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLLEN" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLULEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLULEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLULEN" +end +$have_odbcinst_h = have_header("odbcinst.h") +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLROWOFFSET", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLROWOFFSET manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLROWOFFSET" +end +$have_odbcinst_h = have_header("odbcinst.h") +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLROWSETSIZE", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLROWSETSIZE manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLROWSETSIZE" +end +$have_odbcinst_h = have_header("odbcinst.h") + +if PLATFORM =~ /mswin32/ then + if !have_library_ex("odbc32", "SQLAllocConnect", "sql.h") || + !have_library_ex("odbccp32", "SQLConfigDataSource", "odbcinst.h") || + !have_library_ex("odbccp32", "SQLInstallerError", "odbcinst.h") || + !have_library("user32", "CharUpper") then + puts "Can not locate odbc libraries" + exit 1 + end + have_func("SQLInstallerError", "odbcinst.h") +# mingw untested !!! +elsif PLATFORM =~ /(mingw|cygwin)/ then + have_library("odbc32") + have_library("odbccp32") + have_library("user32") +elsif (testdlopen && PLATFORM !~ /(macos|darwin)/ && CONFIG["CC"] =~ /gcc/ && have_func("dlopen", "dlfcn.h") && have_library("dl", "dlopen")) then + $LDFLAGS+=" -Wl,-init -Wl,ruby_odbc_init -Wl,-fini -Wl,ruby_odbc_fini" + $CPPFLAGS+=" -DHAVE_SQLCONFIGDATASOURCE" + $CPPFLAGS+=" -DHAVE_SQLINSTALLERERROR" + $CPPFLAGS+=" -DUSE_DLOPEN_FOR_ODBC_LIBS" + if defined? have_type then + if have_type("SQLBIGINT", "sqltypes.h", "-DHAVE_LONG_LONG") then + $CPPFLAGS+=" -DHAVE_LONG_LONG" + end + end +else + have_library("odbc", "SQLAllocConnect") || + have_library("iodbc", "SQLAllocConnect") + ($have_odbcinst_h && + have_library("odbcinst", "SQLConfigDataSource")) || + ($have_odbcinst_h && + have_library("iodbcinst", "SQLConfigDataSource")) + $have_odbcinst_h && + have_func("SQLInstallerError", "odbcinst.h") + if defined? have_type then + if have_type("SQLBIGINT", "sqltypes.h", "-DHAVE_LONG_LONG") then + $CPPFLAGS+=" -DHAVE_LONG_LONG" + end + end +end + +create_makefile("odbc") diff --git a/ruby-odbc-0.999991/ext/init.c b/ruby-odbc-0.999991/ext/init.c new file mode 100644 index 0000000..fa40639 --- /dev/null +++ b/ruby-odbc-0.999991/ext/init.c @@ -0,0 +1,197 @@ +/* + * Part of ODBC-Ruby binding + * Copyright (c) 2006-2007 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: init.c,v 1.6 2007/04/07 09:39:08 chw Exp chw $ + */ + +#include "ruby.h" + +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + +/* + * This module acts as a drop-in replacement for linking with + * "-lodbc -lodbcinst" or "-liodbc -liodbcinst" when dlopen() + * is supported. + * + * Setting the environment variable RUBY_ODBC_DM can be used + * to force loading a specific driver manager shared library. + * Same logic is used with RUBY_ODBC_INST for the ODBC installer + * shared library. + */ + +#include + +/* Create weak alias and function declarations. */ + +#define WEAKFUNC(name) \ + int __attribute__((weak, alias("__"#name))) name (void); \ + static int __attribute__((unused)) __ ## name (void) \ + { return -1; /* == SQL_ERROR */ } + +#define WEAKFUNC_BOOL(name) \ + int __attribute__((weak, alias("__"#name))) name (void); \ + static int __attribute__((unused)) __ ## name (void) \ + { return 0; /* == BOOL/FALSE */ } + +WEAKFUNC(SQLAllocConnect) +WEAKFUNC(SQLAllocEnv) +WEAKFUNC(SQLAllocStmt) +WEAKFUNC(SQLBindParameter) +WEAKFUNC(SQLCancel) +WEAKFUNC(SQLDescribeParam) +WEAKFUNC(SQLDisconnect) +WEAKFUNC(SQLExecute) +WEAKFUNC(SQLFetch) +WEAKFUNC(SQLFetchScroll) +WEAKFUNC(SQLFreeConnect) +WEAKFUNC(SQLFreeEnv) +WEAKFUNC(SQLFreeStmt) +WEAKFUNC(SQLGetData) +WEAKFUNC(SQLGetEnvAttr) +WEAKFUNC(SQLGetStmtOption) +WEAKFUNC(SQLMoreResults) +WEAKFUNC(SQLNumParams) +WEAKFUNC(SQLNumResultCols) +WEAKFUNC(SQLRowCount) +WEAKFUNC(SQLSetEnvAttr) +WEAKFUNC(SQLSetStmtOption) +WEAKFUNC(SQLTransact) +WEAKFUNC(SQLEndTran) + +WEAKFUNC(SQLColAttributes) +WEAKFUNC(SQLColAttributesW) +WEAKFUNC(SQLColumns) +WEAKFUNC(SQLColumnsW) +WEAKFUNC(SQLConnect) +WEAKFUNC(SQLConnectW) +WEAKFUNC(SQLDataSources) +WEAKFUNC(SQLDataSourcesW) +WEAKFUNC(SQLDriverConnect) +WEAKFUNC(SQLDriverConnectW) +WEAKFUNC(SQLDrivers) +WEAKFUNC(SQLDriversW) +WEAKFUNC(SQLError) +WEAKFUNC(SQLErrorW) +WEAKFUNC(SQLExecDirect) +WEAKFUNC(SQLExecDirectW) +WEAKFUNC(SQLForeignKeys) +WEAKFUNC(SQLForeignKeysW) +WEAKFUNC(SQLGetConnectOption) +WEAKFUNC(SQLGetConnectOptionW) +WEAKFUNC(SQLGetCursorName) +WEAKFUNC(SQLGetCursorNameW) +WEAKFUNC(SQLGetInfo) +WEAKFUNC(SQLGetInfoW) +WEAKFUNC(SQLGetTypeInfo) +WEAKFUNC(SQLGetTypeInfoW) +WEAKFUNC(SQLPrepare) +WEAKFUNC(SQLPrepareW) +WEAKFUNC(SQLPrimaryKeys) +WEAKFUNC(SQLPrimaryKeysW) +WEAKFUNC(SQLProcedureColumns) +WEAKFUNC(SQLProcedureColumnsW) +WEAKFUNC(SQLProcedures) +WEAKFUNC(SQLProceduresW) +WEAKFUNC(SQLSetConnectOption) +WEAKFUNC(SQLSetConnectOptionW) +WEAKFUNC(SQLSetCursorName) +WEAKFUNC(SQLSetCursorNameW) +WEAKFUNC(SQLSpecialColumns) +WEAKFUNC(SQLSpecialColumnsW) +WEAKFUNC(SQLStatistics) +WEAKFUNC(SQLStatisticsW) +WEAKFUNC(SQLTablePrivileges) +WEAKFUNC(SQLTablePrivilegesW) +WEAKFUNC(SQLTables) +WEAKFUNC(SQLTablesW) +WEAKFUNC(SQLInstallerError) +WEAKFUNC(SQLInstallerErrorW) + +WEAKFUNC_BOOL(SQLConfigDataSource) +WEAKFUNC_BOOL(SQLConfigDataSourceW) +WEAKFUNC_BOOL(SQLReadFileDSN) +WEAKFUNC_BOOL(SQLReadFileDSNW) +WEAKFUNC_BOOL(SQLWriteFileDSN) +WEAKFUNC_BOOL(SQLWriteFileDSNW) + +/* Library initializer and finalizer. */ + +static void *lib_odbc = 0; +static void *lib_odbcinst = 0; + +#define warn(msg) fputs(msg, stderr) + +void +ruby_odbc_init() +{ + int useiodbc = 0; + char *dm_name = getenv("RUBY_ODBC_DM"); + char *inst_name = getenv("RUBY_ODBC_INST"); + + if (dm_name) { + lib_odbc = dlopen(dm_name, RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + warn("WARNING: $RUBY_ODBC_DM not loaded.\n"); + } else { + if (inst_name) { + lib_odbcinst = dlopen(inst_name, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbcinst) { + warn("WARNING: $RUBY_ODBC_INST not loaded.\n"); + } + return; + } + } + lib_odbc = dlopen("libodbc" DLEXT ".1", RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + lib_odbc = dlopen("libodbc" DLEXT, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbc) { + lib_odbc = dlopen("libiodbc" DLEXT ".2", RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbc) { + lib_odbc = dlopen("libiodbc" DLEXT, RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbc) { + warn("WARNING: no ODBC driver manager found.\n"); + return; + } + useiodbc = 1; + } + lib_odbcinst = dlopen(useiodbc ? + "libiodbcinst" DLEXT ".2" : "libodbcinst" DLEXT ".1", + RTLD_NOW | RTLD_GLOBAL); + if (!lib_odbcinst) { + lib_odbcinst = dlopen(useiodbc ? + "libiodbcinst" DLEXT : "libodbcinst" DLEXT, + RTLD_NOW | RTLD_GLOBAL); + } + if (!lib_odbcinst) { + warn("WARNING: no ODBC installer library found.\n"); + } +} + +void +ruby_odbc_fini() +{ + if (lib_odbcinst) { + dlclose(lib_odbcinst); + lib_odbcinst = 0; + } + if (lib_odbc) { + dlclose(lib_odbc); + lib_odbc = 0; + } +} + +int +ruby_odbc_have_func(char *name, void *addr) +{ + return name && addr && (dlsym(NULL, name) != addr); +} + +#endif diff --git a/ruby-odbc-0.999991/ext/odbc.c b/ruby-odbc-0.999991/ext/odbc.c new file mode 100644 index 0000000..098836f --- /dev/null +++ b/ruby-odbc-0.999991/ext/odbc.c @@ -0,0 +1,9475 @@ +/* + * ODBC-Ruby binding + * Copyright (c) 2001-2020 Christian Werner + * Portions copyright (c) 2004 Ryszard Niewisiewicz + * Portions copyright (c) 2006 Carl Blakeley + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: odbc.c,v 1.80 2020/12/25 21:45:42 chw Exp chw $ + */ + +#undef ODBCVER + +#if defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) +#include +#endif +#include +#include +#include "ruby.h" +#ifdef HAVE_VERSION_H +#include "version.h" +#endif +#ifdef HAVE_RUBY_VERSION_H +#include "ruby/version.h" +#endif +#ifdef HAVE_SQL_H +#include +#else +#error Missing include: sql.h +#endif +#ifdef HAVE_SQLEXT_H +#include +#else +#error Missing include: sqlext.h +#endif +#ifdef HAVE_ODBCINST_H +#include +#endif + +#ifdef UNICODE +#include +#endif + +#ifndef HAVE_TYPE_SQLTCHAR +#ifdef UNICODE +typedef SQLWCHAR SQLTCHAR; +#else +typedef SQLCHAR SQLTCHAR; +#endif +#endif + +#ifndef HAVE_TYPE_SQLLEN +#define SQLLEN SQLINTEGER +#endif +#ifndef HAVE_TYPE_SQLULEN +#define SQLULEN SQLUINTEGER +#endif +#ifndef HAVE_TYPE_SQLROWOFFSET +#define SQLROWOFFSET SQLLEN +#endif +#ifndef HAVE_TYPE_SQLROWSETSIZE +#define SQLROWSETSIZE SQLULEN +#endif + +#if (RUBY_VERSION_MAJOR <= 1) && (RUBY_VERSION_MINOR < 9) +#define TIME_USE_USEC 1 +#endif + +#if (RUBY_API_VERSION_CODE >= 20500) +#define FUNCALL_NOARGS(o, m) rb_funcall((o), (m), 0) +#else +#define FUNCALL_NOARGS(o, m) rb_funcall((o), (m), 0, NULL) +#endif + +#ifdef HAVE_RUBY_THREAD_H +#include "ruby/thread.h" +#endif + +/* + * Conditionally undefine aliases of ODBC installer UNICODE functions. + */ + +#if defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW) +#undef SQLInstallerError +#endif +#if defined(UNICODE) && defined(HAVE_SQLCONFIGDATASOURCEW) +#undef SQLConfigDataSource +#endif +#if defined(UNICODE) && defined(HAVE_SQLREADFILEDSNW) +#undef SQLReadFileDSN +#endif +#if defined(UNICODE) && defined(HAVE_SQLWRITEFILEDSNW) +#undef SQLWriteFileDSN +#endif + +#if defined(UNICODE) && defined(USE_DLOPEN_FOR_ODBC_LIBS) +extern int ruby_odbc_have_func(const char *name, void *addr); +#endif + +#ifdef UNICODE +/* + * Declarations of required installer APIs in case + * header files don't provide them (unixODBC?). + */ + +#ifndef HAVE_SQLINSTALLERERRORW +SQLRETURN INSTAPI SQLInstallerErrorW(WORD, DWORD *, LPWSTR, WORD, WORD *); +#endif +#ifndef HAVE_SQLCONFIGDATASOURCEW +BOOL INSTAPI SQLConfigDataSourceW(HWND, WORD, LPWSTR, LPWSTR); +#endif +#ifndef HAVE_SQLREADFILEDSNW +BOOL INSTAPI SQLReadFileDSNW(LPWSTR, LPWSTR, LPWSTR, LPWSTR, WORD, WORD *); +#endif +#ifndef HAVE_SQLWRITEFILEDSNW +BOOL INSTAPI SQLWriteFileDSNW(LPWSTR, LPWSTR, LPWSTR, LPWSTR); +#endif + +#if defined(HAVE_RUBY_ENCODING_H) && HAVE_RUBY_ENCODING_H +#define USE_RB_ENC 1 +#include "ruby/encoding.h" +static rb_encoding *rb_enc = NULL; +static VALUE rb_encv = Qnil; +#endif + +#endif /* UNICODE */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +#define rb_define_alloc_func(cls, func) \ + rb_define_singleton_method(cls, "new", func, -1) +#define rb_undefine_alloc_func(cls) \ + rb_undef_method(CLASS_OF(cls), "new") +#endif + +#ifdef RB_CVAR_SET_4ARGS +#define CVAR_SET(x, y, z) rb_cvar_set(x, y, z, 0) +#else +#define CVAR_SET(x, y, z) rb_cvar_set(x, y, z) +#endif + +#ifndef STR2CSTR +#define STR2CSTR(x) StringValueCStr(x) +#define NO_RB_STR2CSTR 1 +#endif + +#ifdef TRACING +static int tracing = 0; +#define tracemsg(t, x) {if (tracing & t) { x }} +static SQLRETURN tracesql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, + SQLRETURN ret, const char *m); +#else +#define tracemsg(t, x) +#define tracesql(a, b, c, d, e) d +#endif + +#ifndef SQL_SUCCEEDED +#define SQL_SUCCEEDED(x) \ + (((x) == SQL_SUCCESS) || ((x) == SQL_SUCCESS_WITH_INFO)) +#endif + +#ifndef SQL_NO_DATA +#define SQL_NO_DATA SQL_NO_DATA_FOUND +#endif + +typedef struct link { + struct link *succ; + struct link *pred; + struct link *head; + int offs; +} LINK; + +typedef struct env { + VALUE self; + LINK dbcs; + SQLHENV henv; +} ENV; + +typedef struct dbc { + LINK link; + VALUE self; + VALUE env; + struct env *envp; + LINK stmts; + SQLHDBC hdbc; + VALUE rbtime; + VALUE gmtime; + int upc; + VALUE use_sql_column_name; +} DBC; + +typedef struct { + SQLSMALLINT type; + SQLULEN coldef; + SQLULEN coldef_max; + SQLSMALLINT scale; + SQLLEN rlen; + SQLSMALLINT nullable; + SQLSMALLINT iotype; + int override; +#ifdef UNICODE + SQLWCHAR *tofree; +#endif + char buffer[sizeof (double) * 4 + sizeof (TIMESTAMP_STRUCT)]; + SQLSMALLINT ctype; + SQLSMALLINT outtype; + int outsize; + char *outbuf; +} PARAMINFO; + +typedef struct { + int type; + int size; +} COLTYPE; + +typedef struct stmt { + LINK link; + VALUE self; + VALUE dbc; + struct dbc *dbcp; + SQLHSTMT hstmt; + int nump; + PARAMINFO *paraminfo; + int ncols; + COLTYPE *coltypes; + char **colnames; + VALUE *colvals; + char **dbufs; + int fetchc; + int upc; + int usef; +} STMT; + +static VALUE Modbc; +static VALUE Cobj; +static VALUE Cenv; +static VALUE Cdbc; +static VALUE Cstmt; +static VALUE Ccolumn; +static VALUE Cparam; +static VALUE Cerror; +static VALUE Cdsn; +static VALUE Cdrv; +static VALUE Cdate; +static VALUE Ctime; +static VALUE Ctimestamp; +static VALUE Cproc; +static VALUE rb_cDate; + +static ID IDstart; +static ID IDatatinfo; +static ID IDataterror; +static ID IDkeys; +static ID IDatattrs; +static ID IDday; +static ID IDmonth; +static ID IDyear; +static ID IDmday; +static ID IDnsec; +static ID IDusec; +static ID IDsec; +static ID IDmin; +static ID IDhour; +static ID IDusec; +static ID IDkeyp; +static ID IDkey; +static ID IDSymbol; +static ID IDString; +static ID IDFixnum; +static ID IDtable_names; +static ID IDnew; +static ID IDnow; +static ID IDname; +static ID IDtable; +static ID IDtype; +static ID IDlength; +static ID IDnullable; +static ID IDscale; +static ID IDprecision; +static ID IDsearchable; +static ID IDunsigned; +static ID IDiotype; +static ID IDoutput_size; +static ID IDoutput_type; +static ID IDdescr; +static ID IDstatement; +static ID IDreturn_output_param; +static ID IDattrs; +static ID IDNULL; +static ID IDdefault; +#ifdef USE_RB_ENC +static ID IDencode; +#endif +static ID IDparse; +static ID IDutc; +static ID IDlocal; +static ID IDto_s; + +/* + * Modes for dbc_info + */ + +#define INFO_TABLES 0 +#define INFO_COLUMNS 1 +#define INFO_PRIMKEYS 2 +#define INFO_INDEXES 3 +#define INFO_TYPES 4 +#define INFO_FORKEYS 5 +#define INFO_TPRIV 6 +#define INFO_PROCS 7 +#define INFO_PROCCOLS 8 +#define INFO_SPECCOLS 9 + +/* + * Modes for make_result/stmt_exec_int + */ + +#define MAKERES_BLOCK 1 +#define MAKERES_NOCLOSE 2 +#define MAKERES_PREPARE 4 +#define MAKERES_EXECD 8 +#define EXEC_PARMXNULL(x) (16 | ((x) << 5)) +#define EXEC_PARMXOUT(x) (((x) & 16) ? ((x) >> 5) : -1) + +/* + * Modes for do_fetch + */ + +#define DOFETCH_ARY 0 +#define DOFETCH_HASH 1 +#define DOFETCH_HASH2 2 +#define DOFETCH_HASHK 3 +#define DOFETCH_HASHK2 4 +#define DOFETCH_HASHN 5 +#define DOFETCH_MODES 7 +#define DOFETCH_BANG 8 + +/* + * Size of segment when SQL_NO_TOTAL + */ + +#define SEGSIZE 65536 + +/* + * Forward declarations. + */ + +static SQLRETURN callsql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, + SQLRETURN ret, const char *m); + +static VALUE stmt_exec(int argc, VALUE *argv, VALUE self); +static VALUE stmt_each(VALUE self); +static VALUE stmt_each_hash(int argc, VALUE *argv, VALUE self); +static VALUE stmt_close(VALUE self); +static VALUE stmt_drop(VALUE self); + +/* + * Column name buffers on statement. + */ + +static const char *colnamebuf[] = { + "@_c0", "@_c1", "@_c2", "@_c3" +}; + +/* + * Macro to align buffers. + */ + +#define LEN_ALIGN(x) \ + ((x) + sizeof (double) - (((x) + sizeof (double)) % sizeof (double))) + + +/* + *---------------------------------------------------------------------- + * + * Wrappers for long running SQL APIs with GVL released. + * + *---------------------------------------------------------------------- + */ + +#ifdef RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_ + +static void +empty_ubf(void *args) +{ +} + +struct S_SQLCONNECT { + SQLHDBC hdbc; + SQLTCHAR *dsn; + SQLSMALLINT dsn_len; + SQLTCHAR *usr; + SQLSMALLINT usr_len; + SQLTCHAR *pwd; + SQLSMALLINT pwd_len; +}; + +static void * +F_SQLCONNECT(void *args) +{ + size_t ret; + struct S_SQLCONNECT *argp = (struct S_SQLCONNECT *) args; + + ret = SQLConnect(argp->hdbc, argp->dsn, argp->dsn_len, + argp->usr, argp->usr_len, argp->pwd, argp->pwd_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLCONNECT(SQLHDBC hdbc, SQLTCHAR *dsn, SQLSMALLINT dsn_len, SQLTCHAR *usr, + SQLSMALLINT usr_len, SQLTCHAR *pwd, SQLSMALLINT pwd_len) +{ + size_t ret; + struct S_SQLCONNECT arg; + + arg.hdbc = hdbc; + arg.dsn = dsn; + arg.dsn_len = dsn_len; + arg.usr = usr; + arg.usr_len = usr_len; + arg.pwd = pwd; + arg.pwd_len = pwd_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLDRIVERCONNECT { + SQLHDBC hdbc; + SQLHWND hwnd; + SQLTCHAR *connin; + SQLSMALLINT connin_len; + SQLTCHAR *connout; + SQLSMALLINT connout_max; + SQLSMALLINT *connout_len; + SQLUSMALLINT compl; +}; + +static void * +F_SQLDRIVERCONNECT(void *args) +{ + size_t ret; + struct S_SQLDRIVERCONNECT *argp = (struct S_SQLDRIVERCONNECT *) args; + + ret = SQLDriverConnect(argp->hdbc, argp->hwnd, argp->connin, + argp->connin_len, argp->connout, argp->connout_max, + argp->connout_len, argp->compl); + return (void *) ret; +} + +static inline SQLRETURN +SQLDRIVERCONNECT(SQLHDBC hdbc, SQLHWND hwnd, SQLTCHAR *connin, + SQLSMALLINT connin_len, SQLTCHAR *connout, + SQLSMALLINT connout_max, SQLSMALLINT *connout_len, + SQLUSMALLINT compl) +{ + size_t ret; + struct S_SQLDRIVERCONNECT arg; + + arg.hdbc = hdbc; + arg.hwnd = hwnd; + arg.connin = connin; + arg.connin_len = connin_len; + arg.connout = connout; + arg.connout_max = connout_max; + arg.connout_len = connout_len; + arg.compl = compl; + ret = (size_t) rb_thread_call_without_gvl(F_SQLDRIVERCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLDISCONNECT { + SQLHSTMT hstmt; +}; + +static void * +F_SQLDISCONNECT(void *args) +{ + size_t ret; + struct S_SQLDISCONNECT *argp = (struct S_SQLDISCONNECT *) args; + + ret = SQLDisconnect(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLDISCONNECT(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLDISCONNECT arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLDISCONNECT, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLTABLES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLTCHAR *typ; + SQLSMALLINT typ_len; +}; + +static void * +F_SQLTABLES(void *args) +{ + size_t ret; + struct S_SQLTABLES *argp = (struct S_SQLTABLES *) args; + + ret = SQLTables(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->typ, argp->typ_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLTABLES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLTCHAR *typ, SQLSMALLINT typ_len) +{ + size_t ret; + struct S_SQLTABLES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.typ = typ; + arg.typ_len = typ_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTABLES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLCOLUMNS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLTCHAR *col; + SQLSMALLINT col_len; +}; + +static void * +F_SQLCOLUMNS(void *args) +{ + size_t ret; + struct S_SQLCOLUMNS *argp = (struct S_SQLCOLUMNS *) args; + + ret = SQLColumns(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->col, argp->col_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLCOLUMNS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLTCHAR *col, SQLSMALLINT col_len) +{ + size_t ret; + struct S_SQLCOLUMNS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.col = col; + arg.col_len = col_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLCOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPRIMARYKEYS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; +}; + +static void * +F_SQLPRIMARYKEYS(void *args) +{ + size_t ret; + struct S_SQLPRIMARYKEYS *argp = (struct S_SQLPRIMARYKEYS *) args; + + ret = SQLPrimaryKeys(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, argp->tbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPRIMARYKEYS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len) +{ + size_t ret; + struct S_SQLPRIMARYKEYS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPRIMARYKEYS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLFOREIGNKEYS { + SQLHSTMT hdbc; + SQLTCHAR *pkcat; + SQLSMALLINT pkcat_len; + SQLTCHAR *pksch; + SQLSMALLINT pksch_len; + SQLTCHAR *pktbl; + SQLSMALLINT pktbl_len; + SQLTCHAR *fkcat; + SQLSMALLINT fkcat_len; + SQLTCHAR *fksch; + SQLSMALLINT fksch_len; + SQLTCHAR *fktbl; + SQLSMALLINT fktbl_len; +}; + +static void * +F_SQLFOREIGNKEYS(void *args) +{ + size_t ret; + struct S_SQLFOREIGNKEYS *argp = (struct S_SQLFOREIGNKEYS *) args; + + ret = SQLForeignKeys(argp->hdbc, argp->pkcat, argp->pkcat_len, + argp->pksch, argp->pksch_len, + argp->pktbl, argp->pktbl_len, + argp->fkcat, argp->fkcat_len, + argp->fksch, argp->fksch_len, + argp->fktbl, argp->fktbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLFOREIGNKEYS(SQLHDBC hdbc, SQLTCHAR *pkcat, SQLSMALLINT pkcat_len, + SQLTCHAR *pksch, SQLSMALLINT pksch_len, SQLTCHAR *pktbl, + SQLSMALLINT pktbl_len, SQLTCHAR *fkcat, SQLSMALLINT fkcat_len, + SQLTCHAR *fksch, SQLSMALLINT fksch_len, SQLTCHAR *fktbl, + SQLSMALLINT fktbl_len) +{ + size_t ret; + struct S_SQLFOREIGNKEYS arg; + + arg.hdbc = hdbc; + arg.pkcat = pkcat; + arg.pkcat_len = pkcat_len; + arg.pksch = pksch; + arg.pksch_len = pksch_len; + arg.pktbl = pktbl; + arg.pktbl_len = pktbl_len; + arg.fkcat = fkcat; + arg.fkcat_len = fkcat_len; + arg.fksch = fksch; + arg.fksch_len = fksch_len; + arg.fktbl = fktbl; + arg.fktbl_len = fktbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFOREIGNKEYS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPROCEDURES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *prc; + SQLSMALLINT prc_len; +}; + +static void * +F_SQLPROCEDURES(void *args) +{ + size_t ret; + struct S_SQLPROCEDURES *argp = (struct S_SQLPROCEDURES *) args; + + ret = SQLProcedures(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->prc, argp->prc_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPROCEDURES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *prc, + SQLSMALLINT prc_len) +{ + size_t ret; + struct S_SQLPROCEDURES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.prc = prc; + arg.prc_len = prc_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPROCEDURES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLPROCEDURECOLUMNS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *prc; + SQLSMALLINT prc_len; + SQLTCHAR *col; + SQLSMALLINT col_len; +}; + +static void * +F_SQLPROCEDURECOLUMNS(void *args) +{ + size_t ret; + struct S_SQLPROCEDURECOLUMNS *argp = (struct S_SQLPROCEDURECOLUMNS *) args; + + ret = SQLProcedureColumns(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->prc, + argp->prc_len, argp->col, argp->col_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLPROCEDURECOLUMNS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *prc, + SQLSMALLINT prc_len, SQLTCHAR *col, SQLSMALLINT col_len) +{ + size_t ret; + struct S_SQLPROCEDURECOLUMNS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.prc = prc; + arg.prc_len = prc_len; + arg.col = col; + arg.col_len = col_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPROCEDURECOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLTABLEPRIVILEGES { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; +}; + +static void * +F_SQLTABLEPRIVILEGES(void *args) +{ + size_t ret; + struct S_SQLTABLEPRIVILEGES *argp = (struct S_SQLTABLEPRIVILEGES *) args; + + ret = SQLTablePrivileges(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len); + return (void *) ret; +} + +static inline SQLRETURN +SQLTABLEPRIVILEGES(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len) +{ + size_t ret; + struct S_SQLTABLEPRIVILEGES arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTABLEPRIVILEGES, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLSTATISTICS { + SQLHSTMT hdbc; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLUSMALLINT uniq; + SQLUSMALLINT resv; +}; + +static void * +F_SQLSTATISTICS(void *args) +{ + size_t ret; + struct S_SQLSTATISTICS *argp = (struct S_SQLSTATISTICS *) args; + + ret = SQLStatistics(argp->hdbc, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->uniq, argp->resv); + return (void *) ret; +} + +static inline SQLRETURN +SQLSTATISTICS(SQLHDBC hdbc, SQLTCHAR *cat, SQLSMALLINT cat_len, + SQLTCHAR *sch, SQLSMALLINT sch_len, SQLTCHAR *tbl, + SQLSMALLINT tbl_len, SQLUSMALLINT uniq, SQLUSMALLINT resv) +{ + size_t ret; + struct S_SQLSTATISTICS arg; + + arg.hdbc = hdbc; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.uniq = uniq; + arg.resv = resv; + ret = (size_t) rb_thread_call_without_gvl(F_SQLSTATISTICS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLSPECIALCOLUMNS { + SQLHSTMT hdbc; + SQLUSMALLINT idtyp; + SQLTCHAR *cat; + SQLSMALLINT cat_len; + SQLTCHAR *sch; + SQLSMALLINT sch_len; + SQLTCHAR *tbl; + SQLSMALLINT tbl_len; + SQLUSMALLINT scope; + SQLUSMALLINT nulbl; +}; + +static void * +F_SQLSPECIALCOLUMNS(void *args) +{ + size_t ret; + struct S_SQLSPECIALCOLUMNS *argp = (struct S_SQLSPECIALCOLUMNS *) args; + + ret = SQLSpecialColumns(argp->hdbc, argp->idtyp, argp->cat, argp->cat_len, + argp->sch, argp->sch_len, argp->tbl, + argp->tbl_len, argp->scope, argp->nulbl); + return (void *) ret; +} + +static inline SQLRETURN +SQLSPECIALCOLUMNS(SQLHDBC hdbc, SQLUSMALLINT idtyp, SQLTCHAR *cat, + SQLSMALLINT cat_len, SQLTCHAR *sch, SQLSMALLINT sch_len, + SQLTCHAR *tbl, SQLSMALLINT tbl_len, + SQLUSMALLINT scope, SQLUSMALLINT nulbl) +{ + size_t ret; + struct S_SQLSPECIALCOLUMNS arg; + + arg.hdbc = hdbc; + arg.idtyp = idtyp; + arg.cat = cat; + arg.cat_len = cat_len; + arg.sch = sch; + arg.sch_len = sch_len; + arg.tbl = tbl; + arg.tbl_len = tbl_len; + arg.scope = scope; + arg.nulbl = nulbl; + ret = (size_t) rb_thread_call_without_gvl(F_SQLSPECIALCOLUMNS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLGETTYPEINFO { + SQLHSTMT hdbc; + SQLSMALLINT type; +}; + +static void * +F_SQLGETTYPEINFO(void *args) +{ + size_t ret; + struct S_SQLGETTYPEINFO *argp = (struct S_SQLGETTYPEINFO *) args; + + ret = SQLGetTypeInfo(argp->hdbc, argp->type); + return (void *) ret; +} + +static inline SQLRETURN +SQLGETTYPEINFO(SQLHDBC hdbc, SQLSMALLINT type) +{ + size_t ret; + struct S_SQLGETTYPEINFO arg; + + arg.hdbc = hdbc; + arg.type = type; + ret = (size_t) rb_thread_call_without_gvl(F_SQLGETTYPEINFO, &arg, + empty_ubf, &arg); + return ret; +} + +#if (ODBCVER >= 0x0300) + +struct S_SQLENDTRAN { + SQLSMALLINT htype; + SQLHANDLE handle; + SQLSMALLINT op; +}; + +static void * +F_SQLENDTRAN(void *args) +{ + size_t ret; + struct S_SQLENDTRAN *argp = (struct S_SQLENDTRAN *) args; + + ret = SQLEndTran(argp->htype, argp->handle, argp->op); + return (void *) ret; +} + +static inline SQLRETURN +SQLENDTRAN(SQLUSMALLINT htype, SQLHANDLE handle, SQLUSMALLINT op) +{ + size_t ret; + struct S_SQLENDTRAN arg; + + arg.htype = htype; + arg.handle = handle; + arg.op = op; + ret = (size_t) rb_thread_call_without_gvl(F_SQLENDTRAN, &arg, + empty_ubf, &arg); + return ret; +} + +#else + +struct S_SQLTRANSACT { + SQLHENV henv; + SQLHDBC hdbc; + SQLSMALLINT op; +}; + +static void * +F_SQLTRANSACT(void *args) +{ + size_t ret; + struct S_SQLTRANSACT *argp = (struct S_SQLTRANSACT *) args; + + ret = SQLTransact(argp->henv, argp->hdbc, argp->op); + return (void *) ret; +} + +static inline SQLRETURN +SQLTRANSACT(SQLHENV henv, SQLHDBC hdbc, SQLUSMALLINT op) +{ + size_t ret; + struct S_SQLTRANSACT arg; + + arg.henv = henv; + arg.hdbc = hdbc; + arg.op = op; + ret = (size_t) rb_thread_call_without_gvl(F_SQLTRANCACT, &arg, + empty_ubf, &arg); + return ret; +} + +#endif + +struct S_SQLEXECDIRECT { + SQLHSTMT hstmt; + SQLTCHAR *sql; + SQLINTEGER len; +}; + +static void * +F_SQLEXECDIRECT(void *args) +{ + size_t ret; + struct S_SQLEXECDIRECT *argp = (struct S_SQLEXECDIRECT *) args; + + ret = SQLExecDirect(argp->hstmt, argp->sql, argp->len); + return (void *) ret; +} + +static void +F_SQLEXECDIRECT_UBF(void *args) +{ + struct S_SQLEXECDIRECT *argp = (struct S_SQLEXECDIRECT *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLEXECDIRECT(SQLHSTMT hstmt, SQLTCHAR *sql, SQLINTEGER len) +{ + size_t ret; + struct S_SQLEXECDIRECT arg; + + arg.hstmt = hstmt; + arg.sql = sql; + arg.len = len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXECDIRECT, &arg, + F_SQLEXECDIRECT_UBF, &arg); + return ret; +} + +struct S_SQLEXECUTE { + SQLHSTMT hstmt; +}; + +static void * +F_SQLEXECUTE(void *args) +{ + size_t ret; + struct S_SQLEXECUTE *argp = (struct S_SQLEXECUTE *) args; + + ret = SQLExecute(argp->hstmt); + return (void *) ret; +} + +static void +F_SQLEXECUTE_UBF(void *args) +{ + struct S_SQLEXECUTE *argp = (struct S_SQLEXECUTE *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLEXECUTE(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLEXECUTE arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXECUTE, &arg, + F_SQLEXECUTE_UBF, &arg); + return ret; +} + +struct S_SQLPREPARE { + SQLHSTMT hstmt; + SQLTCHAR *sql; + SQLINTEGER len; +}; + +static void * +F_SQLPREPARE(void *args) +{ + size_t ret; + struct S_SQLPREPARE *argp = (struct S_SQLPREPARE *) args; + + ret = SQLPrepare(argp->hstmt, argp->sql, argp->len); + return (void *) ret; +} + +static void +F_SQLPREPARE_UBF(void *args) +{ + struct S_SQLPREPARE *argp = (struct S_SQLPREPARE *) args; + + SQLCancel(argp->hstmt); +} + +static inline SQLRETURN +SQLPREPARE(SQLHSTMT hstmt, SQLTCHAR *sql, SQLINTEGER len) +{ + size_t ret; + struct S_SQLPREPARE arg; + + arg.hstmt = hstmt; + arg.sql = sql; + arg.len = len; + ret = (size_t) rb_thread_call_without_gvl(F_SQLPREPARE, &arg, + F_SQLPREPARE_UBF, &arg); + return ret; +} + +struct S_SQLMORERESULTS { + SQLHSTMT hstmt; +}; + +static void * +F_SQLMORERESULTS(void *args) +{ + size_t ret; + struct S_SQLMORERESULTS *argp = (struct S_SQLMORERESULTS *) args; + + ret = SQLMoreResults(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLMORERESULTS(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLMORERESULTS arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLMORERESULTS, &arg, + empty_ubf, &arg); + return ret; +} + +struct S_SQLFETCH { + SQLHSTMT hstmt; +}; + +static void * +F_SQLFETCH(void *args) +{ + size_t ret; + struct S_SQLFETCH *argp = (struct S_SQLFETCH *) args; + + ret = SQLFetch(argp->hstmt); + return (void *) ret; +} + +static inline SQLRETURN +SQLFETCH(SQLHSTMT hstmt) +{ + size_t ret; + struct S_SQLFETCH arg; + + arg.hstmt = hstmt; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFETCH, &arg, + empty_ubf, &arg); + return ret; +} + +#if (ODBCVER >= 0x0300) + +struct S_SQLFETCHSCROLL { + SQLHSTMT hstmt; + SQLSMALLINT dir; + SQLROWOFFSET offs; +}; + +static void * +F_SQLFETCHSCROLL(void *args) +{ + size_t ret; + struct S_SQLFETCHSCROLL *argp = (struct S_SQLFETCHSCROLL *) args; + + ret = SQLFetchScroll(argp->hstmt, argp->dir, argp->offs); + return (void *) ret; +} + +static inline SQLRETURN +SQLFETCHSCROLL(SQLHSTMT hstmt, SQLSMALLINT dir, SQLROWOFFSET offs) +{ + size_t ret; + struct S_SQLFETCHSCROLL arg; + + arg.hstmt = hstmt; + arg.dir = dir; + arg.offs = offs; + ret = (size_t) rb_thread_call_without_gvl(F_SQLFETCHSCROLL, &arg, + empty_ubf, &arg); + return ret; +} + +#else + +struct S_SQLEXTENDEDFETCH { + SQLHSTMT hstmt; + SQLUSMALLINT type; + SQLROWOFFSET row; + SQLROWSETSIZE *rowp; + SQLUSMALLINT *status +}; + +static void * +F_SQLEXTENDEDFETCH(void *args) +{ + size_t ret; + struct S_SQLEXTENDEDFETCH *argp = (struct S_SQLEXTENDEDFETCH *) args; + + ret = SQLExtendedFetch(argp->hstmt, argp->type, argp->row, argp->rowp, + argp->status); + return (void *) ret; +} + +static inline SQLRETURN +SQLEXTENDEDFETCH(SQLHSTMT hstmt, SQLUSMALLINT type, SQLROWOFFSET row, + SQLROWSETSIZE *rowp, SQLUSMALLINT *status) +{ + size_t ret; + struct S_SQLEXTENDEDFETCH arg; + + arg.hstmt = hstmt; + arg.type = type; + arg.row = row; + arg.rowp = rowp; + arg.status = status; + ret = (size_t) rb_thread_call_without_gvl(F_SQLEXTENDEDFETCH, &arg, + empty_ubf, &arg); + return ret; +} + +#endif + +#else + +#define SQLCONNECT SQLConnect +#define SQLDRIVERCONNECT SQLDriverConnect +#define SQLDISCONNECT SQLDisconnect +#define SQLTABLES SQLTables +#define SQLCOLUMNS SQLColumns +#define SQLPRIMARYKEYS SQLPrimaryKeys +#define SQLFOREIGNKEYS SQLForeignKeys +#define SQLPROCEDURES SQLProcedures +#define SQLPROCEDURECOLUMNS SQLProcedureColumns +#define SQLTABLEPRIVILEGES SQLTablePrivileges +#define SQLSTATISTICS SQLStatistics +#define SQLSPECIALCOLUMNS SQLSpecialColumns +#define SQLGETTYPEINFO SQLGetTypeInfo +#if (ODBCVER >= 0x0300) +#define SQLENDTRAN SQLEndTran +#else +#define SQLTRANSACT SQLTransact +#endif +#define SQLEXECDIRECT SQLExecDirect +#define SQLEXECUTE SQLExecute +#define SQLPREPARE SQLPrepare +#define SQLMORERESULTS SQLMoreResults +#define SQLFETCH SQLFetch +#if (ODBCVER >= 0x0300) +#define SQLFETCHSCROLL SQLFetchScroll +#else +#define SQLEXTENDEDFETCH SQLExtendedFetch +#endif + +#endif + + +/* + *---------------------------------------------------------------------- + * + * UNICODE converters et.al. + * + *---------------------------------------------------------------------- + */ + +#ifdef UNICODE + +static int +uc_strlen(SQLWCHAR *str) +{ + int len = 0; + + if (str != NULL) { + while (*str != '\0') { + ++len; + ++str; + } + } + return len; +} + +static SQLWCHAR * +uc_strchr(SQLWCHAR *str, SQLWCHAR c) +{ + if (str != NULL) { + while ((*str != '\0') && (*str != c)) { + ++str; + } + str = (*str == c) ? str : NULL; + } + return str; +} + +static int +mkutf(char *dest, SQLWCHAR *src, int len) +{ + int i; + char *cp = dest; + + for (i = 0; i < len; i++) { + unsigned long c = src[i]; + + if (sizeof (SQLWCHAR) == (2 * sizeof (char))) { + c &= 0xffff; + } + if (c < 0x80) { + *cp++ = c; + } else if (c < 0x800) { + *cp++ = 0xc0 | ((c >> 6) & 0x1f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (c >= 0xd800) && (c <= 0xdbff) && ((i + 1) < len)) { + unsigned long c2 = src[i + 1] & 0xffff; + + if ((c2 >= 0xdc00) && (c <= 0xdfff)) { + c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000; + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + ++i; + continue; + } + } + *cp++ = 0xe0 | ((c >> 12) & 0x0f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x200000) { + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x4000000) { + *cp++ = 0xf8 | ((c >> 24) & 0x03); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x80000000) { + *cp++ = 0xfc | ((c >> 31) & 0x01); + *cp++ = 0x80 | ((c >> 24) & 0x3f); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } + } + *cp = '\0'; + return cp - dest; +} + +static VALUE +uc_tainted_str_new(SQLWCHAR *str, int len) +{ + VALUE v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } + v = rb_tainted_str_new((cp != NULL) ? cp : "", ulen); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + if (cp != NULL) { + xfree(cp); + } + return v; +} + +static VALUE +uc_tainted_str_new2(SQLWCHAR *str) +{ + return uc_tainted_str_new(str, uc_strlen(str)); +} + +static VALUE +uc_str_new(SQLWCHAR *str, int len) +{ + VALUE v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } +#ifdef USE_RB_ENC + v = rb_enc_str_new((cp != NULL) ? cp : "", ulen, rb_enc); +#else + v = rb_str_new((cp != NULL) ? cp : "", ulen); +#endif + if (cp != NULL) { + xfree(cp); + } + return v; +} + +static VALUE +uc_str_new2(SQLWCHAR *str) +{ + return uc_str_new(str, uc_strlen(str)); +} + +static VALUE +uc_str_cat(VALUE v, SQLWCHAR *str, int len) +{ + VALUE vv = v; + char *cp = xmalloc(len * 6 + 1); + int ulen = 0; + + if ((cp != NULL) && (str != NULL)) { + ulen = mkutf(cp, str, len); + } + if (cp != NULL) { + vv = rb_str_cat(v, cp, ulen); + xfree(cp); + } + return vv; +} + +static SQLWCHAR * +uc_from_utf(unsigned char *str, int len) +{ + SQLWCHAR *uc = NULL; + + if (str != NULL) { + int i = 0; + unsigned char *strend; + + if (len < 0) { + len = strlen((char *) str); + } + strend = str + len; + uc = ALLOC_N(SQLWCHAR, len + 1); + if (uc != NULL) { + while (str < strend) { + unsigned char c = str[0]; + + if (c < 0x80) { + uc[i++] = c; + ++str; + } else if ((c <= 0xc1) || (c >= 0xf5)) { + /* illegal, ignored */ + ++str; + } else if (c < 0xe0) { + if ((str[1] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x1f) << 6) | (str[1] & 0x3f); + + uc[i++] = t; + str += 2; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf0) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x0f) << 12) | + ((str[1] & 0x3f) << 6) | (str[2] & 0x3f); + + uc[i++] = t; + str += 3; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf8) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80) && + ((str[3] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x03) << 18) | + ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | + (str[4] & 0x3f); + + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (t >= 0x10000)) { + t -= 0x10000; + uc[i++] = 0xd800 | (t & 0x3ff); + t = 0xdc00 | ((t >> 10) & 0x3ff); + } + uc[i++] = t; + str += 4; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xfc) { + if (((str[1] & 0xc0) == 0x80) && + ((str[2] & 0xc0) == 0x80) && + ((str[3] & 0xc0) == 0x80) && + ((str[4] & 0xc0) == 0x80)) { + unsigned long t = ((c & 0x01) << 24) | + ((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) | + ((str[4] & 0x3f) << 6) | (str[5] & 0x3f); + + if ((sizeof (SQLWCHAR) == (2 * sizeof (char))) && + (t >= 0x10000)) { + t -= 0x10000; + uc[i++] = 0xd800 | (t & 0x3ff); + t = 0xdc00 | ((t >> 10) & 0x3ff); + } + uc[i++] = t; + str += 5; + } else { + uc[i++] = c; + ++str; + } + } else { + /* ignore */ + ++str; + } + } + uc[i] = 0; + } + } + return uc; +} + +static void +uc_free(SQLWCHAR *str) +{ + if (str != NULL) { + xfree(str); + } +} + +#endif + + +/* + *---------------------------------------------------------------------- + * + * Things for ODBC::DSN + * + *---------------------------------------------------------------------- + */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +dsn_new(VALUE self) +{ + VALUE obj = rb_obj_alloc(Cdsn); + + rb_obj_call_init(obj, 0, NULL); + return obj; +} +#endif + +static VALUE +dsn_init(VALUE self) +{ + rb_iv_set(self, "@name", Qnil); + rb_iv_set(self, "@descr", Qnil); + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Things for ODBC::Driver + * + *---------------------------------------------------------------------- + */ + +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +drv_new(VALUE self) +{ + VALUE obj = rb_obj_alloc(Cdrv); + + rb_obj_call_init(obj, 0, NULL); + return obj; +} +#endif + +static VALUE +drv_init(VALUE self) +{ + rb_iv_set(self, "@name", Qnil); + rb_iv_set(self, "@attrs", rb_hash_new()); + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Cleanup routines and GC mark/free callbacks. + * + *---------------------------------------------------------------------- + */ + +static void +list_init(LINK *link, int offs) +{ + link->succ = link->pred = link->head = NULL; + link->offs = offs; +} + +static void +list_add(LINK *link, LINK *head) +{ + if (link->head != NULL) { + rb_fatal("RubyODBC: already in list"); + } + if (head == NULL) { + rb_fatal("RubyODBC: invalid list head"); + } + link->head = head; + link->pred = NULL; + link->succ = head->succ; + head->succ = link; + if (link->succ != NULL) { + link->succ->pred = link; + } +} + +static void +list_del(LINK *link) +{ + if (link == NULL) { + rb_fatal("RubyODBC: invalid list item"); + } + if (link->head == NULL) { + rb_fatal("RubyODBC: item not in list"); + } + if (link->succ != NULL) { + link->succ->pred = link->pred; + } + if (link->pred != NULL) { + link->pred->succ = link->succ; + } else { + link->head->succ = link->succ; + } + link->succ = link->pred = link->head = NULL; +} + +static void * +list_first(LINK *head) +{ + if (head->succ == NULL) { + return NULL; + } + return (void *) ((char *) head->succ - head->offs); +} + +static int +list_empty(LINK *head) +{ + return head->succ == NULL; +} + +static void +free_env(ENV *e) +{ + e->self = Qnil; + if (!list_empty(&e->dbcs)) { + return; + } + tracemsg(2, fprintf(stderr, "ObjFree: ENV %p\n", e);); + if (e->henv != SQL_NULL_HENV) { + callsql(SQL_NULL_HENV, e->henv, SQL_NULL_HSTMT, + SQLFreeEnv(e->henv), "SQLFreeEnv"); + e->henv = SQL_NULL_HENV; + } + xfree(e); +} + +static void +link_dbc(DBC *p, ENV *e) +{ + p->envp = e; + list_add(&p->link, &e->dbcs); +} + +static void +unlink_dbc(DBC *p) +{ + if (p == NULL) { + return; + } + p->env = Qnil; + if (p->envp != NULL) { + ENV *e = p->envp; + + list_del(&p->link); + if (e->self == Qnil) { + free_env(e); + } + p->envp = NULL; + } +} + +static void +free_dbc(DBC *p) +{ + p->self = p->env = Qnil; + if (!list_empty(&p->stmts)) { + return; + } + tracemsg(2, fprintf(stderr, "ObjFree: DBC %p\n", p);); + if (p->hdbc != SQL_NULL_HDBC) { + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLDISCONNECT(p->hdbc), "SQLDisconnect"); + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLFreeConnect(p->hdbc), "SQLFreeConnect"); + p->hdbc = SQL_NULL_HDBC; + } + unlink_dbc(p); + xfree(p); +} + +static void +free_stmt_sub(STMT *q, int withp) +{ + int i; + + if (withp) { + if (q->paraminfo != NULL) { + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].outbuf != NULL) { + xfree(q->paraminfo[i].outbuf); + } + } + xfree(q->paraminfo); + q->paraminfo = NULL; + } + q->nump = 0; + } + q->ncols = 0; + if (q->coltypes != NULL) { + xfree(q->coltypes); + q->coltypes = NULL; + } + if (q->colnames != NULL) { + xfree(q->colnames); + q->colnames = NULL; + } + if (q->colvals != NULL) { + xfree(q->colvals); + q->colvals = NULL; + } + if (q->dbufs != NULL) { + xfree(q->dbufs); + q->dbufs = NULL; + } + if (q->self != Qnil) { + VALUE v; + + v = rb_iv_get(q->self, "@_a"); + if (v != Qnil) { + rb_ary_clear(v); + } + v = rb_iv_get(q->self, "@_h"); + if (v != Qnil) { + rb_iv_set(q->self, "@_h", rb_hash_new()); + } + for (i = 0; i < 4; i++) { + v = rb_iv_get(q->self, colnamebuf[i]); + if (v != Qnil) { + rb_iv_set(q->self, colnamebuf[i], rb_hash_new()); + } + } + } +} + +static void +link_stmt(STMT *q, DBC *p) +{ + q->dbcp = p; + list_add(&q->link, &p->stmts); +} + +static void +unlink_stmt(STMT *q) +{ + if (q == NULL) { + return; + } + q->dbc = Qnil; + if (q->dbcp != NULL) { + DBC *p = q->dbcp; + + list_del(&q->link); + if (p->self == Qnil) { + free_dbc(p); + } + q->dbcp = NULL; + } +} + +static void +free_stmt(STMT *q) +{ + VALUE qself = q->self; + + q->self = q->dbc = Qnil; + free_stmt_sub(q, 1); + tracemsg(2, fprintf(stderr, "ObjFree: STMT %p\n", q);); + if (q->hstmt != SQL_NULL_HSTMT) { + /* Issue warning message. */ + fprintf(stderr, "WARNING: # was not dropped" + " before garbage collection.\n", (long) qself); + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + } + unlink_stmt(q); + xfree(q); +} + +static void +start_gc() +{ + FUNCALL_NOARGS(rb_mGC, IDstart); +} + +static void +mark_dbc(DBC *p) +{ + if (p->env != Qnil) { + rb_gc_mark(p->env); + } +} + +static void +mark_stmt(STMT *q) +{ + if (q->dbc != Qnil) { + rb_gc_mark(q->dbc); + } +} + +/* + *---------------------------------------------------------------------- + * + * Set internal error (or warning) message. + * + *---------------------------------------------------------------------- + */ + +static char * +set_err(const char *msg, int warn) +{ + VALUE a, v = rb_str_new2("INTERN (0) [RubyODBC]"); + + v = rb_str_cat2(v, msg); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + a = rb_ary_new2(1); + rb_ary_push(a, rb_obj_taint(v)); + CVAR_SET(Cobj, warn ? IDatatinfo : IDataterror, a); + return STR2CSTR(v); +} + +/* + *---------------------------------------------------------------------- + * + * Functions to retrieve last SQL error or warning. + * + *---------------------------------------------------------------------- + */ + +static char * +get_err_or_info(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, int isinfo) +{ +#ifdef UNICODE + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#else + SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#endif + char buf[32], tmp[SQL_MAX_MESSAGE_LENGTH]; + SQLRETURN err; + SQLINTEGER nativeerr; + SQLSMALLINT len; + VALUE v0 = Qnil, a = Qnil, v; + int done = 0; + + while (!done) { + v = Qnil; + err = tracesql(henv, hdbc, hstmt, + SQLError(henv, hdbc, hstmt, state, &nativeerr, msg, + SQL_MAX_MESSAGE_LENGTH - 1, &len), + "SQLError"); + state[6] = '\0'; + msg[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; + switch (err) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: +#ifdef UNICODE + v = uc_str_new2(state); +#else + v = rb_str_new2((char *) state); +#endif + sprintf(buf, " (%d) ", (int) nativeerr); + v = rb_str_cat2(v, buf); +#ifdef UNICODE + v = uc_str_cat(v, msg, len); +#else + v = rb_str_cat(v, (char *) msg, len); +#endif + break; + case SQL_NO_DATA: + if ((v0 == Qnil) && (!isinfo)) { + v = rb_str_new2("INTERN (0) [RubyODBC]No data found"); + } else { + v = Qnil; + } + done = 1; + break; + case SQL_INVALID_HANDLE: + v = rb_str_new2("INTERN (0) [RubyODBC]Invalid handle"); + done = 1; + break; + case SQL_ERROR: + v = rb_str_new2("INTERN (0) [RubyODBC]Error reading error message"); + done = 1; + break; + default: + sprintf(tmp, "INTERN (0) [RubyODBC]Unknown error %d", (int) err); + v = rb_str_new2(tmp); + done = 1; + break; + } + if (v != Qnil) { + if (v0 == Qnil) { + v0 = v; + a = rb_ary_new(); + } + rb_ary_push(a, rb_obj_taint(v)); + tracemsg(1, fprintf(stderr, " | %s\n", STR2CSTR(v));); + } + } + CVAR_SET(Cobj, isinfo ? IDatatinfo : IDataterror, a); + if (isinfo) { + return NULL; + } + return (v0 == Qnil) ? NULL : STR2CSTR(v0); +} + +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) +static char * +get_installer_err() +{ +#ifdef UNICODE +#ifdef HAVE_SQLINSTALLERERRORW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH]; +#else + char msg[SQL_MAX_MESSAGE_LENGTH]; +#endif + char buf[128]; + SQLRETURN err; + VALUE v0 = Qnil, a = Qnil, v; + int done = 0; + WORD i, len; + DWORD insterrcode; + + for (i = 1; (!done) && (i <= 8); i++) { + v = Qnil; +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLInstallerErrorW", SQLInstallerErrorW); +#endif + if (have_w) { + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerErrorW(i, &insterrcode, msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerErrorW"); + msg[SQL_MAX_MESSAGE_LENGTH - 1] = 0; + } else { + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerError(i, &insterrcode, (char *) msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerErrorW"); + ((char *) msg)[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; + } +#else + err = tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLInstallerError(i, &insterrcode, msg, + SQL_MAX_MESSAGE_LENGTH, &len), + "SQLInstallerError"); + msg[SQL_MAX_MESSAGE_LENGTH - 1] = '\0'; +#endif + switch (err) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + sprintf(buf, "INSTALLER (%d) ", (int) insterrcode); + v = rb_str_new2(buf); +#ifdef UNICODE + if (have_w) { +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + v = uc_str_cat(v, msg, len); + } else { + v = rb_str_cat(v, (char *) msg, len); + } +#else + v = rb_str_cat(v, msg, len); +#endif + break; + case SQL_NO_DATA: + done = 1; + break; + case SQL_ERROR: + v = rb_str_new2("INTERN (0) [RubyODBC]"); + v = rb_str_cat2(v, "Error reading installer error message"); + done = 1; + break; + default: + v = rb_str_new2("INTERN (0) [RubyODBC]"); + sprintf(buf, "Unknown installer error %d", (int) err); + v = rb_str_cat2(v, buf); + done = 1; + break; + } + if (v != Qnil) { + if (v0 == Qnil) { + v0 = v; + a = rb_ary_new(); + } + rb_ary_push(a, rb_obj_taint(v)); + tracemsg(1, fprintf(stderr, " | %s\n", STR2CSTR(v));); + } + } + CVAR_SET(Cobj, IDataterror, a); + return (v0 == Qnil) ? NULL : STR2CSTR(v0); +} +#endif + +static char * +get_err(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt) +{ + return get_err_or_info(henv, hdbc, hstmt, 0); +} + +#ifdef TRACING +static void +trace_sql_ret(SQLRETURN ret) +{ + char msg[32]; + const char *p; + + switch (ret) { + case SQL_SUCCESS: + p = "SQL_SUCCESS"; + break; + case SQL_SUCCESS_WITH_INFO: + p = "SQL_SUCCESS_WITH_INFO"; + break; + case SQL_NO_DATA: + p = "SQL_NO_DATA"; + break; + case SQL_ERROR: + p = "SQL_ERROR"; + break; + case SQL_INVALID_HANDLE: + p = "SQL_INVALID_HANDLE"; + break; + default: + sprintf(msg, "SQL_RETURN=%d", (int) ret); + p = msg; + break; + } + fprintf(stderr, " < %s\n", p); +} + +static SQLRETURN +tracesql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + const char *m) +{ + if (tracing & 1) { + fprintf(stderr, "SQLCall: %s", m); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } + return ret; +} +#endif + +static SQLRETURN +callsql(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + const char *m) +{ + SQLRETURN err; + + err = tracesql(henv, hdbc, hstmt, ret, m); + if (err != SQL_SUCCESS) { +#ifdef UNICODE + SQLWCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#else + SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH], state[6 + 1]; +#endif + SQLINTEGER nativeerr; + SQLSMALLINT len; + int done = 0; + + while (!done) { + err = tracesql(henv, hdbc, hstmt, + SQLError(henv, hdbc, hstmt, state, &nativeerr, msg, + SQL_MAX_MESSAGE_LENGTH - 1, &len), + "SQLError"); + switch (err) { + case SQL_SUCCESS: + break; + case SQL_NO_DATA: + case SQL_INVALID_HANDLE: + case SQL_ERROR: + default: + done = 1; + break; + } + } + } + return ret; +} + +static int +succeeded_common(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp) +{ + if (!SQL_SUCCEEDED(ret)) { + char *dummy; + + if (msgp == NULL) { + msgp = &dummy; + } + *msgp = get_err_or_info(henv, hdbc, hstmt, 0); + return 0; + } + if (ret == SQL_SUCCESS_WITH_INFO) { + get_err_or_info(henv, hdbc, hstmt, 1); + } else { + CVAR_SET(Cobj, IDatatinfo, Qnil); + } + return 1; +} + +static int +succeeded(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp, const char *m, ...) +{ +#ifdef TRACING + va_list args; + + if (tracing & 1) { + va_start(args, m); + fprintf(stderr, "SQLCall: "); + vfprintf(stderr, m, args); + va_end(args); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } +#endif + return succeeded_common(henv, hdbc, hstmt, ret, msgp); +} + +static int +succeeded_nodata(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLRETURN ret, + char **msgp, const char *m, ...) +{ +#ifdef TRACING + va_list args; + + if (tracing & 1) { + va_start(args, m); + fprintf(stderr, "SQLCall: "); + vfprintf(stderr, m, args); + va_end(args); + fprintf(stderr, "\n > HENV=0x%lx, HDBC=0x%lx, HSTMT=0x%lx\n", + (long) henv, (long) hdbc, (long) hstmt); + trace_sql_ret(ret); + } +#endif + if (ret == SQL_NO_DATA) { + CVAR_SET(Cobj, IDatatinfo, Qnil); + return 1; + } + return succeeded_common(henv, hdbc, hstmt, ret, msgp); +} + +/* + *---------------------------------------------------------------------- + * + * Return ENV from VALUE. + * + *---------------------------------------------------------------------- + */ + +static VALUE +env_of(VALUE self) +{ + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + self = q->dbc; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + } + if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *p; + + Data_Get_Struct(self, DBC, p); + self = p->env; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Database", 0)); + } + } + return self; +} + +static ENV * +get_env(VALUE self) +{ + ENV *e; + + Data_Get_Struct(env_of(self), ENV, e); + return e; +} + +/* + *---------------------------------------------------------------------- + * + * Return DBC from VALUE. + * + *---------------------------------------------------------------------- + */ + +static DBC * +get_dbc(VALUE self) +{ + DBC *p; + + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + self = q->dbc; + if (self == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + } + Data_Get_Struct(self, DBC, p); + return p; +} + +/* + *---------------------------------------------------------------------- + * + * Raise ODBC error from Ruby. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_raise(VALUE self, VALUE msg) +{ + VALUE a, v; + char buf[SQL_MAX_MESSAGE_LENGTH + 1], *p; + + if (TYPE(msg) == T_STRING) { + v = msg; + } else { + v = rb_any_to_s(msg); + } + strcpy(buf, "INTERN (1) [RubyODBC]"); + p = STR2CSTR(v); + strncat(buf, p, SQL_MAX_MESSAGE_LENGTH - strlen(buf)); + buf[SQL_MAX_MESSAGE_LENGTH] = '\0'; + v = rb_str_new2(buf); + a = rb_ary_new2(1); + rb_ary_push(a, rb_obj_taint(v)); + CVAR_SET(Cobj, IDataterror, a); + rb_raise(Cerror, "%s", buf); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain an ENV. + * + *---------------------------------------------------------------------- + */ + +static VALUE +env_new(VALUE self) +{ + ENV *e; + SQLHENV henv = SQL_NULL_HENV; + VALUE obj; + + if (TYPE(self) == T_MODULE) { + self = Cobj; + } + if (self == Cobj) { + self = Cenv; + } + if ((!SQL_SUCCEEDED(SQLAllocEnv(&henv))) || (henv == SQL_NULL_HENV)) { + rb_raise(Cerror, "%s", set_err("Cannot allocate SQLHENV", 0)); + } + obj = Data_Make_Struct(self, ENV, NULL, free_env, e); + tracemsg(2, fprintf(stderr, "ObjAlloc: ENV %p\n", e);); + e->self = obj; + e->henv = henv; + list_init(&e->dbcs, offsetof(DBC, link)); +#if defined(UNICODE) && defined(SQL_OV_ODBC3) + callsql(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, + (SQLPOINTER) SQL_OV_ODBC3, 0), + "SQLSetEnvAttr(SQL_OV_ODBC3)"); +#endif + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain array of known DSNs. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_dsns(VALUE self) +{ +#ifdef UNICODE + SQLWCHAR dsn[SQL_MAX_DSN_LENGTH], descr[SQL_MAX_MESSAGE_LENGTH * 2]; +#else + char dsn[SQL_MAX_DSN_LENGTH], descr[SQL_MAX_MESSAGE_LENGTH * 2]; +#endif + SQLSMALLINT dsnLen = 0, descrLen = 0; + int first = 1; + VALUE env, aret; + ENV *e; + + env = env_new(Cenv); + Data_Get_Struct(env, ENV, e); + aret = rb_ary_new(); + while (succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLDataSources(e->henv, (SQLUSMALLINT) (first ? + SQL_FETCH_FIRST : SQL_FETCH_NEXT), + (SQLTCHAR *) dsn, + (SQLSMALLINT) sizeof (dsn), &dsnLen, + (SQLTCHAR *) descr, + (SQLSMALLINT) sizeof (descr), + &descrLen), + NULL, "SQLDataSources")) { + VALUE odsn = rb_obj_alloc(Cdsn); + +#ifdef UNICODE + dsnLen = (dsnLen == 0) ? (SQLSMALLINT) uc_strlen(dsn) : + (SQLSMALLINT) (dsnLen / sizeof (SQLWCHAR)); + descrLen = (descrLen == 0) ? + (SQLSMALLINT) uc_strlen(descr) : + (SQLSMALLINT) (descrLen / sizeof (SQLWCHAR)); + rb_iv_set(odsn, "@name", uc_tainted_str_new(dsn, dsnLen)); + rb_iv_set(odsn, "@descr", uc_tainted_str_new(descr, descrLen)); +#else + dsnLen = (dsnLen == 0) ? (SQLSMALLINT) strlen(dsn) : dsnLen; + descrLen = (descrLen == 0) ? (SQLSMALLINT) strlen(descr) : descrLen; + rb_iv_set(odsn, "@name", rb_tainted_str_new(dsn, dsnLen)); + rb_iv_set(odsn, "@descr", rb_tainted_str_new(descr, descrLen)); +#endif + rb_ary_push(aret, odsn); + first = dsnLen = descrLen = 0; + } + return aret; +} + +/* + *---------------------------------------------------------------------- + * + * Obtain array of known drivers. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_drivers(VALUE self) +{ +#ifdef UNICODE + SQLWCHAR driver[SQL_MAX_MESSAGE_LENGTH], attrs[SQL_MAX_MESSAGE_LENGTH * 2]; + SQLWCHAR *attr; +#else + char driver[SQL_MAX_MESSAGE_LENGTH], attrs[SQL_MAX_MESSAGE_LENGTH * 2]; + char *attr; +#endif + SQLSMALLINT driverLen = 0, attrsLen = 0; + int first = 1; + VALUE env, aret; + ENV *e; + + env = env_new(Cenv); + Data_Get_Struct(env, ENV, e); + aret = rb_ary_new(); + while (succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLDrivers(e->henv, (SQLUSMALLINT) (first ? + SQL_FETCH_FIRST : SQL_FETCH_NEXT), + (SQLTCHAR *) driver, + (SQLSMALLINT) sizeof (driver), &driverLen, + (SQLTCHAR *) attrs, + (SQLSMALLINT) sizeof (attrs), &attrsLen), + NULL, "SQLDrivers")) { + VALUE odrv = rb_obj_alloc(Cdrv); + VALUE h = rb_hash_new(); + int count = 0; + +#ifdef UNICODE + driverLen = (driverLen == 0) ? + (SQLSMALLINT) uc_strlen(driver) : + (SQLSMALLINT) (driverLen / sizeof (SQLWCHAR)); + rb_iv_set(odrv, "@name", uc_tainted_str_new(driver, driverLen)); + for (attr = attrs; *attr; attr += uc_strlen(attr) + 1) { + SQLWCHAR *p = uc_strchr(attr, (SQLWCHAR) '='); + + if ((p != NULL) && (p != attr)) { + rb_hash_aset(h, + uc_tainted_str_new(attr, (p - attr) / + sizeof (SQLWCHAR)), + uc_tainted_str_new2(p + 1)); + count++; + } + } +#else + driverLen = (driverLen == 0) ? (SQLSMALLINT) strlen(driver) : driverLen; + rb_iv_set(odrv, "@name", rb_tainted_str_new(driver, driverLen)); + for (attr = attrs; *attr; attr += strlen(attr) + 1) { + char *p = strchr(attr, '='); + + if ((p != NULL) && (p != attr)) { + rb_hash_aset(h, rb_tainted_str_new(attr, p - attr), + rb_tainted_str_new2(p + 1)); + count++; + } + } +#endif + if (count > 0) { + rb_iv_set(odrv, "@attrs", h); + } + rb_ary_push(aret, odrv); + first = driverLen = attrsLen = 0; + } + return aret; +} + +/* + *---------------------------------------------------------------------- + * + * Management methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_ODBCINST_H +static VALUE +conf_dsn(int argc, VALUE *argv, VALUE self, int op) +{ + VALUE drv, attr, issys, astr; +#ifdef UNICODE +#ifdef HAVE_SQLCONFIGDATASOURCEW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sdrv, *sastr; +#else + char *sdrv, *sastr; +#endif + + rb_scan_args(argc, argv, "12", &drv, &attr, &issys); + if (rb_obj_is_kind_of(drv, Cdrv) == Qtrue) { + VALUE a, x; + + if (argc > 2) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + x = rb_iv_get(drv, "@name"); + a = rb_iv_get(drv, "@attrs"); + issys = attr; + drv = x; + attr = a; + } + Check_Type(drv, T_STRING); + if (RTEST(issys)) { + switch (op) { + case ODBC_ADD_DSN: op = ODBC_ADD_SYS_DSN; break; + case ODBC_CONFIG_DSN: op = ODBC_CONFIG_SYS_DSN; break; + case ODBC_REMOVE_DSN: op = ODBC_REMOVE_SYS_DSN; break; + } + } + astr = rb_str_new2(""); + if (rb_obj_is_kind_of(attr, rb_cHash) == Qtrue) { + VALUE a, x; + + a = FUNCALL_NOARGS(attr, IDkeys); + while ((x = rb_ary_shift(a)) != Qnil) { + VALUE v = rb_hash_aref(attr, x); + + astr = rb_str_concat(astr, x); + astr = rb_str_cat2(astr, "="); + astr = rb_str_concat(astr, v); + astr = rb_str_cat(astr, "", 1); + } + } + astr = rb_str_cat(astr, "", 1); +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLConfigDataSourceW", SQLConfigDataSourceW); +#endif + if (have_w) { +#ifdef USE_RB_ENC + drv = rb_funcall(drv, IDencode, 1, rb_encv); + astr = rb_funcall(astr, IDencode, 1, rb_encv); +#endif + sdrv = uc_from_utf((unsigned char *) STR2CSTR(drv), -1); + sastr = uc_from_utf((unsigned char *) STR2CSTR(astr), -1); + if ((sdrv == NULL) || (sastr == NULL)) { + uc_free(sdrv); + uc_free(sastr); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + if (SQLConfigDataSourceW(NULL, (WORD) op, + (LPWSTR) sdrv, (LPWSTR) sastr)) { + uc_free(sdrv); + uc_free(sastr); + return Qnil; + } + uc_free(sdrv); + uc_free(sastr); + } else { + sdrv = (SQLWCHAR *) STR2CSTR(drv); + sastr = (SQLWCHAR *) STR2CSTR(astr); + if (SQLConfigDataSource(NULL, (WORD) op, + (LPCSTR) sdrv, (LPCSTR) sastr)) { + return Qnil; + } + } +#else + sdrv = STR2CSTR(drv); + sastr = STR2CSTR(astr); + if (SQLConfigDataSource(NULL, (WORD) op, sdrv, sastr)) { + return Qnil; + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("DSN configuration error", 0)); +#endif + return Qnil; +} +#endif + +static VALUE +dbc_adddsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_ADD_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::add_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_confdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_CONFIG_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::config_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_deldsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + return conf_dsn(argc, argv, self, ODBC_REMOVE_DSN); +#else + rb_raise(Cerror, "%s", set_err("ODBC::del_dsn not supported", 0)); + return Qnil; +#endif +} + +static VALUE +dbc_wfdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + VALUE fname, aname, kname, val; +#ifdef UNICODE +#ifdef HAVE_SQLWRITEFILEDSNW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sfname, *saname, *skname, *sval = NULL; +#else + char *sfname, *saname, *skname, *sval = NULL; +#endif + + rb_scan_args(argc, argv, "31", &fname, &aname, &kname, &val); + Check_Type(fname, T_STRING); + Check_Type(aname, T_STRING); + Check_Type(kname, T_STRING); + if (val != Qnil) { + Check_Type(val, T_STRING); + } +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLWriteFileDSNW", SQLWriteFileDSNW); +#endif + if (have_w) { + BOOL rc; + +#ifdef USE_RB_ENC + fname = rb_funcall(fname, IDencode, 1, rb_encv); + aname = rb_funcall(aname, IDencode, 1, rb_encv); + kname = rb_funcall(kname, IDencode, 1, rb_encv); + if (val != Qnil) { + val = rb_funcall(val, IDencode, 1, rb_encv); + } +#endif + sfname = uc_from_utf((unsigned char *) STR2CSTR(fname), -1); + saname = uc_from_utf((unsigned char *) STR2CSTR(aname), -1); + skname = uc_from_utf((unsigned char *) STR2CSTR(kname), -1); + if ((sfname == NULL) || (saname == NULL) || (skname == NULL)) { +nomem: + uc_free(sfname); + uc_free(saname); + uc_free(skname); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + if (val != Qnil) { + sval = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (sval == NULL) { + goto nomem; + } + } + rc = SQLWriteFileDSNW(sfname, saname, skname, sval); + uc_free(sfname); + uc_free(saname); + uc_free(skname); + uc_free(sval); + if (rc) { + return Qnil; + } + } else { + sfname = (SQLWCHAR *) STR2CSTR(fname); + saname = (SQLWCHAR *) STR2CSTR(aname); + skname = (SQLWCHAR *) STR2CSTR(kname); + if (val != Qnil) { + sval = (SQLWCHAR *) STR2CSTR(val); + } + if (SQLWriteFileDSN((LPCSTR) sfname, (LPCSTR) saname, + (LPCSTR) skname, (LPCSTR) sval)) { + return Qnil; + } + } +#else + sfname = STR2CSTR(fname); + saname = STR2CSTR(aname); + skname = STR2CSTR(kname); + if (val != Qnil) { + sval = STR2CSTR(val); + } + if (SQLWriteFileDSN(sfname, saname, skname, sval)) { + return Qnil; + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("File DSN configuration error", 0)); +#endif +#else + rb_raise(Cerror, "%s", set_err("ODBC::write_file_dsn not supported", 0)); +#endif + return Qnil; +} + +static VALUE +dbc_rfdsn(int argc, VALUE *argv, VALUE self) +{ +#ifdef HAVE_ODBCINST_H + VALUE fname, aname, kname; +#ifdef UNICODE +#ifdef HAVE_SQLREADFILEDSNW + int have_w = 1; +#else + int have_w = 0; +#endif + SQLWCHAR *sfname, *saname, *skname, valbuf[SQL_MAX_MESSAGE_LENGTH]; +#else + char *sfname, *saname, *skname, valbuf[SQL_MAX_MESSAGE_LENGTH]; +#endif + + rb_scan_args(argc, argv, "30", &fname, &aname, &kname); + Check_Type(fname, T_STRING); + Check_Type(aname, T_STRING); + Check_Type(kname, T_STRING); +#ifdef UNICODE +#ifdef USE_DLOPEN_FOR_ODBC_LIBS + have_w = ruby_odbc_have_func("SQLReadFileDSNW", SQLReadFileDSNW); +#endif + if (have_w) { + BOOL rc; + +#ifdef USE_RB_ENC + fname = rb_funcall(fname, IDencode, 1, rb_encv); + aname = rb_funcall(aname, IDencode, 1, rb_encv); + kname = rb_funcall(kname, IDencode, 1, rb_encv); +#endif + sfname = uc_from_utf((unsigned char *) STR2CSTR(fname), -1); + saname = uc_from_utf((unsigned char *) STR2CSTR(aname), -1); + skname = uc_from_utf((unsigned char *) STR2CSTR(kname), -1); + valbuf[0] = 0; + if ((sfname == NULL) || (saname == NULL) || (skname == NULL)) { + uc_free(sfname); + uc_free(saname); + uc_free(skname); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + rc = SQLReadFileDSNW(sfname, saname, skname, valbuf, + sizeof (valbuf), NULL); + uc_free(sfname); + uc_free(saname); + uc_free(skname); + if (rc) { + return uc_tainted_str_new2(valbuf); + } + } else { + sfname = (SQLWCHAR *) STR2CSTR(fname); + saname = (SQLWCHAR *) STR2CSTR(aname); + skname = (SQLWCHAR *) STR2CSTR(kname); + valbuf[0] = '\0'; + if (SQLReadFileDSN((LPCSTR) sfname, (LPCSTR) saname, + (LPCSTR) skname, (LPSTR) valbuf, + sizeof (valbuf), NULL)) { + return rb_tainted_str_new2((char *) valbuf); + } + } +#else + sfname = STR2CSTR(fname); + saname = STR2CSTR(aname); + skname = STR2CSTR(kname); + valbuf[0] = '\0'; + if (SQLReadFileDSN(sfname, saname, skname, valbuf, + sizeof (valbuf), NULL)) { + return rb_tainted_str_new2(valbuf); + } +#endif +#if defined(HAVE_SQLINSTALLERERROR) || (defined(UNICODE) && defined(HAVE_SQLINSTALLERERRORW)) + rb_raise(Cerror, "%s", set_err(get_installer_err(), 0)); +#else + rb_raise(Cerror, "%s", set_err("File DSN configuration error", 0)); +#endif +#else + rb_raise(Cerror, "%s", set_err("ODBC::read_file_dsn not supported", 0)); + return Qnil; +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Return last ODBC error or warning. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_error(VALUE self) +{ + return rb_cvar_get(Cobj, IDataterror); +} + +static VALUE +dbc_warn(VALUE self) +{ + return rb_cvar_get(Cobj, IDatatinfo); +} + +static VALUE +dbc_clrerror(VALUE self) +{ + CVAR_SET(Cobj, IDataterror, Qnil); + CVAR_SET(Cobj, IDatatinfo, Qnil); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Connection instance initializer. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +dbc_alloc(VALUE self) +{ + DBC *p; + VALUE obj = Data_Make_Struct(self, DBC, mark_dbc, free_dbc, p); + + tracemsg(2, fprintf(stderr, "ObjAlloc: DBC %p\n", p);); + list_init(&p->link, offsetof(DBC, link)); + p->self = obj; + p->env = Qnil; + p->envp = NULL; + list_init(&p->stmts, offsetof(STMT, link)); + p->hdbc = SQL_NULL_HDBC; + p->rbtime = Qfalse; + p->gmtime = Qfalse; + return obj; +} +#endif + +static VALUE +dbc_new(int argc, VALUE *argv, VALUE self) +{ + DBC *p; + VALUE obj, env = Qnil; + + if (TYPE(self) == T_MODULE) { + self = Cobj; + } + if (self == Cobj) { + self = Cdbc; + } + if (rb_obj_is_kind_of(self, Cenv) == Qtrue) { + env = env_of(self); + self = Cdbc; + } +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + obj = rb_obj_alloc(Cdbc); + Data_Get_Struct(obj, DBC, p); + p->env = env; +#else + obj = Data_Make_Struct(self, DBC, mark_dbc, free_dbc, p); + tracemsg(2, fprintf(stderr, "ObjAlloc: DBC %p\n", p);); + list_init(&p->link, offsetof(DBC, link)); + p->self = obj; + p->env = env; + p->envp = NULL; + list_init(&p->stmts, offsetof(STMT, link)); + p->hdbc = SQL_NULL_HDBC; + p->upc = 0; + p->use_sql_column_name = Qfalse; +#endif + if (env != Qnil) { + ENV *e; + + Data_Get_Struct(env, ENV, e); + link_dbc(p, e); + } + if (argc > 0) { + rb_obj_call_init(obj, argc, argv); + } + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Connect to data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_connect(int argc, VALUE *argv, VALUE self) +{ + ENV *e; + DBC *p; + VALUE dsn, user, passwd; +#ifdef UNICODE + SQLWCHAR *sdsn = NULL, *suser = NULL, *spasswd = NULL; +#else + char *sdsn, *suser = NULL, *spasswd = NULL; +#endif + char *msg; + SQLHDBC dbc; + + rb_scan_args(argc, argv, "03", &dsn, &user, &passwd); + if (dsn != Qnil) { + if (rb_obj_is_kind_of(dsn, Cdsn) == Qtrue) { + dsn = rb_iv_get(dsn, "@name"); + } + Check_Type(dsn, T_STRING); + } + if (user != Qnil) { + Check_Type(user, T_STRING); + } + if (passwd != Qnil) { + Check_Type(passwd, T_STRING); + } + p = get_dbc(self); + if (p->hdbc != SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("Already connected", 0)); + } + if (p->env == Qnil) { + p->env = env_new(Cenv); + e = get_env(p->env); + link_dbc(p, e); + } else { + e = get_env(p->env); + } + if (dsn == Qnil) { + return self; + } +#ifdef UNICODE + if (user != Qnil) { +#ifdef USE_RB_ENC + user = rb_funcall(user, IDencode, 1, rb_encv); +#endif + suser = uc_from_utf((unsigned char *) STR2CSTR(user), -1); + } + if (passwd != Qnil) { +#ifdef USE_RB_ENC + passwd = rb_funcall(passwd, IDencode, 1, rb_encv); +#endif + spasswd = uc_from_utf((unsigned char *) STR2CSTR(passwd), -1); + } +#ifdef USE_RB_ENC + dsn = rb_funcall(dsn, IDencode, 1, rb_encv); +#endif + sdsn = uc_from_utf((unsigned char *) STR2CSTR(dsn), -1); + if (((suser == NULL) && (user != Qnil)) || + ((spasswd == NULL) && (passwd != Qnil)) || + (sdsn == NULL)) { + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + if (user != Qnil) { + suser = STR2CSTR(user); + } + if (passwd != Qnil) { + spasswd = STR2CSTR(passwd); + } + sdsn = STR2CSTR(dsn); +#endif + if (!succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLAllocConnect(e->henv, &dbc), &msg, "SQLAllocConnect")) { +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + rb_raise(Cerror, "%s", msg); + } + if (!succeeded(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLCONNECT(dbc, (SQLTCHAR *) sdsn, SQL_NTS, + (SQLTCHAR *) suser, + (SQLSMALLINT) (suser ? SQL_NTS : 0), + (SQLTCHAR *) spasswd, + (SQLSMALLINT) (spasswd ? SQL_NTS : 0)), + &msg, + "SQLConnect('%s')", sdsn)) { +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + callsql(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLFreeConnect(dbc), "SQLFreeConnect"); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(sdsn); + uc_free(suser); + uc_free(spasswd); +#endif + p->hdbc = dbc; + return self; +} + +static VALUE +dbc_drvconnect(VALUE self, VALUE drv) +{ + ENV *e; + DBC *p; +#ifdef UNICODE + SQLWCHAR *sdrv; +#else + char *sdrv; +#endif + char *msg; + SQLHDBC dbc; + + if (rb_obj_is_kind_of(drv, Cdrv) == Qtrue) { + VALUE d, a, x; + + d = rb_str_new2(""); + a = FUNCALL_NOARGS(rb_iv_get(drv, "@attrs"), IDkeys); + while ((x = rb_ary_shift(a)) != Qnil) { + VALUE v = rb_hash_aref(rb_iv_get(drv, "@attrs"), x); + + d = rb_str_concat(d, x); + d = rb_str_cat2(d, "="); + d = rb_str_concat(d, v); + d = rb_str_cat2(d, ";"); + } + drv = d; + } + Check_Type(drv, T_STRING); + p = get_dbc(self); + if (p->hdbc != SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("Already connected", 0)); + } + if (p->env == Qnil) { + p->env = env_new(Cenv); + e = get_env(p->env); + link_dbc(p, e); + } else { + e = get_env(p->env); + } +#ifdef UNICODE +#ifdef USE_RB_ENC + drv = rb_funcall(drv, IDencode, 1, rb_encv); +#endif + sdrv = uc_from_utf((unsigned char *) STR2CSTR(drv), -1); + if (sdrv == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + sdrv = STR2CSTR(drv); +#endif + if (!succeeded(e->henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLAllocConnect(e->henv, &dbc), &msg, "SQLAllocConnect")) { +#ifdef UNICODE + uc_free(sdrv); +#endif + rb_raise(Cerror, "%s", msg); + } + if (!succeeded(e->henv, dbc, SQL_NULL_HSTMT, + SQLDRIVERCONNECT(dbc, NULL, (SQLTCHAR *) sdrv, SQL_NTS, + NULL, 0, NULL, SQL_DRIVER_NOPROMPT), + &msg, "SQLDriverConnect")) { +#ifdef UNICODE + uc_free(sdrv); +#endif + callsql(SQL_NULL_HENV, dbc, SQL_NULL_HSTMT, + SQLFreeConnect(dbc), "SQLFreeConnect"); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(sdrv); +#endif + p->hdbc = dbc; + return self; +} + +static VALUE +dbc_connected(VALUE self) +{ + DBC *p = get_dbc(self); + + return (p->hdbc == SQL_NULL_HDBC) ? Qfalse : Qtrue; +} + +static VALUE +dbc_timefmt(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->rbtime = (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->rbtime; +} + +static VALUE +dbc_timeutc(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->gmtime = (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->gmtime; +} + +static VALUE +dbc_use_scn(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE val; + + if (argc > 0) { + rb_scan_args(argc, argv, "1", &val); + p->use_sql_column_name = + (val != Qnil && val != Qfalse) ? Qtrue : Qfalse; + } + return p->use_sql_column_name; +} + +/* + *---------------------------------------------------------------------- + * + * Drop all active statements from data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_dropall(VALUE self) +{ + DBC *p = get_dbc(self); + + while (!list_empty(&p->stmts)) { + STMT *q = list_first(&p->stmts); + + if (q->self == Qnil) { + rb_fatal("RubyODBC: invalid stmt in dropall"); + } + stmt_drop(q->self); + } + return self; +} + +/* + *---------------------------------------------------------------------- + * + * Disconnect from data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_disconnect(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE nodrop = Qfalse; + char *msg; + + rb_scan_args(argc, argv, "01", &nodrop); + if (!RTEST(nodrop)) { + dbc_dropall(self); + } + if (p->hdbc == SQL_NULL_HDBC) { + return Qtrue; + } + if (list_empty(&p->stmts)) { + callsql(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLDISCONNECT(p->hdbc), "SQLDisconnect"); + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLFreeConnect(p->hdbc), &msg, "SQLFreeConnect")) { + rb_raise(Cerror, "%s", msg); + } + p->hdbc = SQL_NULL_HDBC; + unlink_dbc(p); + start_gc(); + return Qtrue; + } + return Qfalse; +} + +/* + *---------------------------------------------------------------------- + * + * Database meta data via SQLGetInfo() + * + *---------------------------------------------------------------------- + */ + +#ifndef SQL_DTC_TRANSITION_COST +#define SQL_DTC_TRANSITION_COST 1750 +#endif +#ifndef SQL_DTC_ENLIST_EXPENSIZE +#define SQL_DTC_ENLIST_EXPENDSIZE 1 +#endif +#ifndef SQL_DTC_UNENLIST_EXPENSIZE +#define SQL_DTC_UNENLIST_EXPENDSIZE 2 +#endif + +#define GI_CONST_SINT(x) { #x, x, SQL_C_SHORT } +#define GI_CONST_INT(x) { #x, x, SQL_C_LONG } +#define GI_CONST_BITS(x) { #x, x, SQL_C_LONG } +#define GI_CONST_STR(x) { #x, x, SQL_C_CHAR } +#define GI_CONST_END { NULL, -1, -1 } +static struct { + const char *name; + int info; + int maptype; +} get_info_map[] = { + + /* yielding ints */ + GI_CONST_SINT(SQL_ACTIVE_ENVIRONMENTS), + GI_CONST_SINT(SQL_ACTIVE_CONNECTIONS), + GI_CONST_SINT(SQL_ACTIVE_STATEMENTS), + GI_CONST_INT(SQL_ASYNC_MODE), + GI_CONST_SINT(SQL_CATALOG_LOCATION), + GI_CONST_SINT(SQL_CONCAT_NULL_BEHAVIOR), + GI_CONST_SINT(SQL_CORRELATION_NAME), + GI_CONST_SINT(SQL_CURSOR_COMMIT_BEHAVIOR), + GI_CONST_SINT(SQL_CURSOR_ROLLBACK_BEHAVIOR), + GI_CONST_INT(SQL_CURSOR_SENSITIVITY), + GI_CONST_INT(SQL_DDL_INDEX), + GI_CONST_INT(SQL_DEFAULT_TXN_ISOLATION), + GI_CONST_INT(SQL_DRIVER_HDBC), + GI_CONST_INT(SQL_DRIVER_HENV), + GI_CONST_INT(SQL_DRIVER_HDESC), + GI_CONST_INT(SQL_DRIVER_HLIB), + GI_CONST_INT(SQL_DRIVER_HSTMT), + GI_CONST_SINT(SQL_FILE_USAGE), + GI_CONST_SINT(SQL_GROUP_BY), + GI_CONST_SINT(SQL_IDENTIFIER_CASE), + GI_CONST_INT(SQL_MAX_ASYNC_CONCURRENT_STATEMENTS), + GI_CONST_INT(SQL_MAX_BINARY_LITERAL_LEN), + GI_CONST_SINT(SQL_MAX_CATALOG_NAME_LEN), + GI_CONST_INT(SQL_MAX_CHAR_LITERAL_LEN), + GI_CONST_SINT(SQL_MAX_COLUMN_NAME_LEN), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_GROUP_BY), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_INDEX), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_ORDER_BY), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_SELECT), + GI_CONST_SINT(SQL_MAX_COLUMNS_IN_TABLE), + GI_CONST_SINT(SQL_MAX_CONCURRENT_ACTIVITIES), + GI_CONST_SINT(SQL_MAX_CURSOR_NAME_LEN), + GI_CONST_SINT(SQL_MAX_DRIVER_CONNECTIONS), + GI_CONST_SINT(SQL_MAX_IDENTIFIER_LEN), + GI_CONST_INT(SQL_MAX_INDEX_SIZE), + GI_CONST_SINT(SQL_MAX_OWNER_NAME_LEN), + GI_CONST_SINT(SQL_MAX_PROCEDURE_NAME_LEN), + GI_CONST_SINT(SQL_MAX_QUALIFIER_NAME_LEN), + GI_CONST_INT(SQL_MAX_ROW_SIZE), + GI_CONST_SINT(SQL_MAX_SCHEMA_NAME_LEN), + GI_CONST_INT(SQL_MAX_STATEMENT_LEN), + GI_CONST_SINT(SQL_MAX_TABLE_NAME_LEN), + GI_CONST_SINT(SQL_MAX_TABLES_IN_SELECT), + GI_CONST_SINT(SQL_MAX_USER_NAME_LEN), + GI_CONST_SINT(SQL_NON_NULLABLE_COLUMNS), + GI_CONST_SINT(SQL_NULL_COLLATION), + GI_CONST_SINT(SQL_ODBC_API_CONFORMANCE), + GI_CONST_INT(SQL_ODBC_INTERFACE_CONFORMANCE), + GI_CONST_SINT(SQL_ODBC_SAG_CLI_CONFORMANCE), + GI_CONST_SINT(SQL_ODBC_SQL_CONFORMANCE), + GI_CONST_INT(SQL_PARAM_ARRAY_ROW_COUNTS), + GI_CONST_INT(SQL_PARAM_ARRAY_SELECTS), + GI_CONST_SINT(SQL_QUALIFIER_LOCATION), + GI_CONST_SINT(SQL_QUOTED_IDENTIFIER_CASE), + GI_CONST_INT(SQL_SQL_CONFORMANCE), + GI_CONST_SINT(SQL_TXN_CAPABLE), + + /* yielding ints (but bitmasks) */ + GI_CONST_BITS(SQL_AGGREGATE_FUNCTIONS), + GI_CONST_BITS(SQL_ALTER_DOMAIN), + GI_CONST_BITS(SQL_ALTER_TABLE), + GI_CONST_BITS(SQL_BATCH_ROW_COUNT), + GI_CONST_BITS(SQL_BATCH_SUPPORT), + GI_CONST_BITS(SQL_BOOKMARK_PERSISTENCE), + GI_CONST_BITS(SQL_CATALOG_USAGE), + GI_CONST_BITS(SQL_CONVERT_BINARY), + GI_CONST_BITS(SQL_CONVERT_BIT), + GI_CONST_BITS(SQL_CONVERT_CHAR), +#ifdef SQL_CONVERT_GUID + GI_CONST_BITS(SQL_CONVERT_GUID), +#endif + GI_CONST_BITS(SQL_CONVERT_DATE), + GI_CONST_BITS(SQL_CONVERT_DECIMAL), + GI_CONST_BITS(SQL_CONVERT_DOUBLE), + GI_CONST_BITS(SQL_CONVERT_FLOAT), + GI_CONST_BITS(SQL_CONVERT_FUNCTIONS), + GI_CONST_BITS(SQL_CONVERT_INTEGER), + GI_CONST_BITS(SQL_CONVERT_INTERVAL_YEAR_MONTH), + GI_CONST_BITS(SQL_CONVERT_INTERVAL_DAY_TIME), + GI_CONST_BITS(SQL_CONVERT_LONGVARBINARY), + GI_CONST_BITS(SQL_CONVERT_LONGVARCHAR), + GI_CONST_BITS(SQL_CONVERT_NUMERIC), + GI_CONST_BITS(SQL_CONVERT_REAL), + GI_CONST_BITS(SQL_CONVERT_SMALLINT), + GI_CONST_BITS(SQL_CONVERT_TIME), + GI_CONST_BITS(SQL_CONVERT_TIMESTAMP), + GI_CONST_BITS(SQL_CONVERT_TINYINT), + GI_CONST_BITS(SQL_CONVERT_VARBINARY), + GI_CONST_BITS(SQL_CONVERT_VARCHAR), + GI_CONST_BITS(SQL_CONVERT_WCHAR), + GI_CONST_BITS(SQL_CONVERT_WLONGVARCHAR), + GI_CONST_BITS(SQL_CONVERT_WVARCHAR), + GI_CONST_BITS(SQL_CREATE_ASSERTION), + GI_CONST_BITS(SQL_CREATE_CHARACTER_SET), + GI_CONST_BITS(SQL_CREATE_COLLATION), + GI_CONST_BITS(SQL_CREATE_DOMAIN), + GI_CONST_BITS(SQL_CREATE_SCHEMA), + GI_CONST_BITS(SQL_CREATE_TABLE), + GI_CONST_BITS(SQL_CREATE_TRANSLATION), + GI_CONST_BITS(SQL_CREATE_VIEW), + GI_CONST_BITS(SQL_DATETIME_LITERALS), + GI_CONST_BITS(SQL_DROP_ASSERTION), + GI_CONST_BITS(SQL_DROP_CHARACTER_SET), + GI_CONST_BITS(SQL_DROP_COLLATION), + GI_CONST_BITS(SQL_DROP_DOMAIN), + GI_CONST_BITS(SQL_DROP_SCHEMA), + GI_CONST_BITS(SQL_DROP_TABLE), + GI_CONST_BITS(SQL_DROP_TRANSLATION), + GI_CONST_BITS(SQL_DROP_VIEW), + GI_CONST_BITS(SQL_DTC_TRANSITION_COST), + GI_CONST_BITS(SQL_DYNAMIC_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_DYNAMIC_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_FETCH_DIRECTION), + GI_CONST_BITS(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_GETDATA_EXTENSIONS), + GI_CONST_BITS(SQL_KEYSET_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_KEYSET_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_INDEX_KEYWORDS), + GI_CONST_BITS(SQL_INFO_SCHEMA_VIEWS), + GI_CONST_BITS(SQL_INSERT_STATEMENT), + GI_CONST_BITS(SQL_LOCK_TYPES), + GI_CONST_BITS(SQL_NUMERIC_FUNCTIONS), + GI_CONST_BITS(SQL_OJ_CAPABILITIES), + GI_CONST_BITS(SQL_OWNER_USAGE), + GI_CONST_BITS(SQL_POS_OPERATIONS), + GI_CONST_BITS(SQL_POSITIONED_STATEMENTS), + GI_CONST_BITS(SQL_QUALIFIER_USAGE), + GI_CONST_BITS(SQL_SCHEMA_USAGE), + GI_CONST_BITS(SQL_SCROLL_CONCURRENCY), + GI_CONST_BITS(SQL_SCROLL_OPTIONS), + GI_CONST_BITS(SQL_SQL92_DATETIME_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_FOREIGN_KEY_DELETE_RULE), + GI_CONST_BITS(SQL_SQL92_FOREIGN_KEY_UPDATE_RULE), + GI_CONST_BITS(SQL_SQL92_GRANT), + GI_CONST_BITS(SQL_SQL92_NUMERIC_VALUE_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_PREDICATES), + GI_CONST_BITS(SQL_SQL92_RELATIONAL_JOIN_OPERATORS), + GI_CONST_BITS(SQL_SQL92_REVOKE), + GI_CONST_BITS(SQL_SQL92_ROW_VALUE_CONSTRUCTOR), + GI_CONST_BITS(SQL_SQL92_STRING_FUNCTIONS), + GI_CONST_BITS(SQL_SQL92_VALUE_EXPRESSIONS), + GI_CONST_BITS(SQL_STANDARD_CLI_CONFORMANCE), + GI_CONST_BITS(SQL_STATIC_CURSOR_ATTRIBUTES1), + GI_CONST_BITS(SQL_STATIC_CURSOR_ATTRIBUTES2), + GI_CONST_BITS(SQL_STATIC_SENSITIVITY), + GI_CONST_BITS(SQL_STRING_FUNCTIONS), + GI_CONST_BITS(SQL_SUBQUERIES), + GI_CONST_BITS(SQL_SYSTEM_FUNCTIONS), + GI_CONST_BITS(SQL_TIMEDATE_ADD_INTERVALS), + GI_CONST_BITS(SQL_TIMEDATE_DIFF_INTERVALS), + GI_CONST_BITS(SQL_TIMEDATE_FUNCTIONS), + GI_CONST_BITS(SQL_TXN_ISOLATION_OPTION), + GI_CONST_BITS(SQL_UNION), + + /* yielding strings */ + GI_CONST_STR(SQL_ACCESSIBLE_PROCEDURES), + GI_CONST_STR(SQL_ACCESSIBLE_TABLES), + GI_CONST_STR(SQL_CATALOG_NAME), + GI_CONST_STR(SQL_CATALOG_NAME_SEPARATOR), + GI_CONST_STR(SQL_CATALOG_TERM), + GI_CONST_STR(SQL_COLLATION_SEQ), + GI_CONST_STR(SQL_COLUMN_ALIAS), + GI_CONST_STR(SQL_DATA_SOURCE_NAME), + GI_CONST_STR(SQL_DATA_SOURCE_READ_ONLY), + GI_CONST_STR(SQL_DATABASE_NAME), + GI_CONST_STR(SQL_DBMS_NAME), + GI_CONST_STR(SQL_DBMS_VER), + GI_CONST_STR(SQL_DESCRIBE_PARAMETER), + GI_CONST_STR(SQL_DM_VER), + GI_CONST_STR(SQL_DRIVER_NAME), + GI_CONST_STR(SQL_DRIVER_ODBC_VER), + GI_CONST_STR(SQL_DRIVER_VER), + GI_CONST_STR(SQL_EXPRESSIONS_IN_ORDERBY), + GI_CONST_STR(SQL_IDENTIFIER_QUOTE_CHAR), + GI_CONST_STR(SQL_INTEGRITY), + GI_CONST_STR(SQL_KEYWORDS), + GI_CONST_STR(SQL_LIKE_ESCAPE_CLAUSE), + GI_CONST_STR(SQL_MAX_ROW_SIZE_INCLUDES_LONG), + GI_CONST_STR(SQL_MULT_RESULT_SETS), + GI_CONST_STR(SQL_MULTIPLE_ACTIVE_TXN), + GI_CONST_STR(SQL_NEED_LONG_DATA_LEN), + GI_CONST_STR(SQL_ODBC_SQL_OPT_IEF), + GI_CONST_STR(SQL_ODBC_VER), + GI_CONST_STR(SQL_ORDER_BY_COLUMNS_IN_SELECT), + GI_CONST_STR(SQL_OUTER_JOINS), + GI_CONST_STR(SQL_OWNER_TERM), + GI_CONST_STR(SQL_PROCEDURE_TERM), + GI_CONST_STR(SQL_PROCEDURES), + GI_CONST_STR(SQL_QUALIFIER_NAME_SEPARATOR), + GI_CONST_STR(SQL_QUALIFIER_TERM), + GI_CONST_STR(SQL_ROW_UPDATES), + GI_CONST_STR(SQL_SCHEMA_TERM), + GI_CONST_STR(SQL_SEARCH_PATTERN_ESCAPE), + GI_CONST_STR(SQL_SERVER_NAME), + GI_CONST_STR(SQL_SPECIAL_CHARACTERS), + GI_CONST_STR(SQL_TABLE_TERM), + GI_CONST_STR(SQL_USER_NAME), + GI_CONST_STR(SQL_XOPEN_CLI_YEAR), + + /* end of table */ + GI_CONST_END +}; + +#define GI_CONST_BITMAP(x) { #x, x } +#define GI_CONST_BITMAP_END { NULL, 0 } +static struct { + const char *name; + int bits; +} get_info_bitmap[] = { + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_AD_ADD_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_AD_ADD_DOMAIN_CONSTRAINT), + GI_CONST_BITMAP(SQL_AD_ADD_DOMAIN_DEFAULT), + GI_CONST_BITMAP(SQL_AD_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_AD_DROP_DOMAIN_CONSTRAINT), + GI_CONST_BITMAP(SQL_AD_DROP_DOMAIN_DEFAULT), + GI_CONST_BITMAP(SQL_AF_ALL), + GI_CONST_BITMAP(SQL_AF_AVG), + GI_CONST_BITMAP(SQL_AF_COUNT), + GI_CONST_BITMAP(SQL_AF_DISTINCT), + GI_CONST_BITMAP(SQL_AF_MAX), + GI_CONST_BITMAP(SQL_AF_MIN), + GI_CONST_BITMAP(SQL_AF_SUM), + GI_CONST_BITMAP(SQL_AM_CONNECTION), + GI_CONST_BITMAP(SQL_AM_NONE), + GI_CONST_BITMAP(SQL_AM_STATEMENT), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_COLLATION), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_AT_ADD_COLUMN_SINGLE), + GI_CONST_BITMAP(SQL_AT_ADD_CONSTRAINT), + GI_CONST_BITMAP(SQL_AT_ADD_TABLE_CONSTRAINT), +#ifdef SQL_AT_COLUMN_SINGLE + GI_CONST_BITMAP(SQL_AT_COLUMN_SINGLE), +#endif + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_AT_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_CASCADE), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_AT_DROP_COLUMN_RESTRICT), + GI_CONST_BITMAP(SQL_AT_DROP_TABLE_CONSTRAINT_CASCADE), + GI_CONST_BITMAP(SQL_AT_DROP_TABLE_CONSTRAINT_RESTRICT), + GI_CONST_BITMAP(SQL_AT_SET_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_BP_CLOSE), + GI_CONST_BITMAP(SQL_BP_DELETE), + GI_CONST_BITMAP(SQL_BP_DROP), + GI_CONST_BITMAP(SQL_BP_OTHER_HSTMT), + GI_CONST_BITMAP(SQL_BP_SCROLL), + GI_CONST_BITMAP(SQL_BP_TRANSACTION), + GI_CONST_BITMAP(SQL_BP_UPDATE), + GI_CONST_BITMAP(SQL_BRC_EXPLICIT), + GI_CONST_BITMAP(SQL_BRC_PROCEDURES), + GI_CONST_BITMAP(SQL_BRC_ROLLED_UP), + GI_CONST_BITMAP(SQL_BS_ROW_COUNT_EXPLICIT), + GI_CONST_BITMAP(SQL_BS_ROW_COUNT_PROC), + GI_CONST_BITMAP(SQL_BS_SELECT_EXPLICIT), + GI_CONST_BITMAP(SQL_BS_SELECT_PROC), + GI_CONST_BITMAP(SQL_CA1_ABSOLUTE), + GI_CONST_BITMAP(SQL_CA1_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_ADD), + GI_CONST_BITMAP(SQL_CA1_BULK_DELETE_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_FETCH_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_BULK_UPDATE_BY_BOOKMARK), + GI_CONST_BITMAP(SQL_CA1_LOCK_EXCLUSIVE), + GI_CONST_BITMAP(SQL_CA1_LOCK_NO_CHANGE), + GI_CONST_BITMAP(SQL_CA1_LOCK_UNLOCK), + GI_CONST_BITMAP(SQL_CA1_NEXT), + GI_CONST_BITMAP(SQL_CA1_POS_DELETE), + GI_CONST_BITMAP(SQL_CA1_POSITIONED_DELETE), + GI_CONST_BITMAP(SQL_CA1_POSITIONED_UPDATE), + GI_CONST_BITMAP(SQL_CA1_POS_POSITION), + GI_CONST_BITMAP(SQL_CA1_POS_REFRESH), + GI_CONST_BITMAP(SQL_CA1_POS_UPDATE), + GI_CONST_BITMAP(SQL_CA1_RELATIVE), + GI_CONST_BITMAP(SQL_CA1_SELECT_FOR_UPDATE), + GI_CONST_BITMAP(SQL_CA2_CRC_APPROXIMATE), + GI_CONST_BITMAP(SQL_CA2_CRC_EXACT), + GI_CONST_BITMAP(SQL_CA2_LOCK_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_AFFECTS_ALL), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_CATALOG), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_DELETE), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_INSERT), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_SELECT), + GI_CONST_BITMAP(SQL_CA2_MAX_ROWS_UPDATE), + GI_CONST_BITMAP(SQL_CA2_OPT_ROWVER_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_OPT_VALUES_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_READ_ONLY_CONCURRENCY), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_ADDITIONS), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_DELETIONS), + GI_CONST_BITMAP(SQL_CA2_SENSITIVITY_UPDATES), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_NON_UNIQUE), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_TRY_UNIQUE), + GI_CONST_BITMAP(SQL_CA2_SIMULATE_UNIQUE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CA_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CA_CREATE_ASSERTION), + GI_CONST_BITMAP(SQL_CB_CLOSE), + GI_CONST_BITMAP(SQL_CB_DELETE), + GI_CONST_BITMAP(SQL_CB_NON_NULL), + GI_CONST_BITMAP(SQL_CB_NULL), + GI_CONST_BITMAP(SQL_CB_PRESERVE), + GI_CONST_BITMAP(SQL_CC_CLOSE), + GI_CONST_BITMAP(SQL_CC_DELETE), + GI_CONST_BITMAP(SQL_CCOL_CREATE_COLLATION), + GI_CONST_BITMAP(SQL_CC_PRESERVE), + GI_CONST_BITMAP(SQL_CCS_COLLATE_CLAUSE), + GI_CONST_BITMAP(SQL_CCS_CREATE_CHARACTER_SET), + GI_CONST_BITMAP(SQL_CCS_LIMITED_COLLATION), + GI_CONST_BITMAP(SQL_CDO_COLLATION), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_CDO_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CDO_CREATE_DOMAIN), + GI_CONST_BITMAP(SQL_CDO_DEFAULT), + GI_CONST_BITMAP(SQL_CL_END), + GI_CONST_BITMAP(SQL_CL_START), + GI_CONST_BITMAP(SQL_CN_ANY), + GI_CONST_BITMAP(SQL_CN_DIFFERENT), + GI_CONST_BITMAP(SQL_CN_NONE), + GI_CONST_BITMAP(SQL_CONCUR_TIMESTAMP), + GI_CONST_BITMAP(SQL_CR_CLOSE), + GI_CONST_BITMAP(SQL_CR_DELETE), + GI_CONST_BITMAP(SQL_CR_PRESERVE), + GI_CONST_BITMAP(SQL_CS_AUTHORIZATION), + GI_CONST_BITMAP(SQL_CS_CREATE_SCHEMA), + GI_CONST_BITMAP(SQL_CS_DEFAULT_CHARACTER_SET), + GI_CONST_BITMAP(SQL_CT_COLUMN_COLLATION), + GI_CONST_BITMAP(SQL_CT_COLUMN_CONSTRAINT), + GI_CONST_BITMAP(SQL_CT_COLUMN_DEFAULT), + GI_CONST_BITMAP(SQL_CT_COMMIT_DELETE), + GI_CONST_BITMAP(SQL_CT_COMMIT_PRESERVE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_DEFERRABLE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_INITIALLY_DEFERRED), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_NAME_DEFINITION), + GI_CONST_BITMAP(SQL_CT_CONSTRAINT_NON_DEFERRABLE), + GI_CONST_BITMAP(SQL_CT_CREATE_TABLE), + GI_CONST_BITMAP(SQL_CT_GLOBAL_TEMPORARY), + GI_CONST_BITMAP(SQL_CT_LOCAL_TEMPORARY), + GI_CONST_BITMAP(SQL_CTR_CREATE_TRANSLATION), + GI_CONST_BITMAP(SQL_CT_TABLE_CONSTRAINT), + GI_CONST_BITMAP(SQL_CU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_CU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_CU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_CU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_CU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_CV_CASCADED), + GI_CONST_BITMAP(SQL_CV_CHECK_OPTION), + GI_CONST_BITMAP(SQL_CV_CREATE_VIEW), + GI_CONST_BITMAP(SQL_CV_LOCAL), + GI_CONST_BITMAP(SQL_CVT_BIGINT), + GI_CONST_BITMAP(SQL_CVT_BINARY), + GI_CONST_BITMAP(SQL_CVT_BIT), + GI_CONST_BITMAP(SQL_CVT_CHAR), + GI_CONST_BITMAP(SQL_CVT_DATE), + GI_CONST_BITMAP(SQL_CVT_DECIMAL), + GI_CONST_BITMAP(SQL_CVT_DOUBLE), + GI_CONST_BITMAP(SQL_CVT_FLOAT), + GI_CONST_BITMAP(SQL_CVT_INTEGER), + GI_CONST_BITMAP(SQL_CVT_INTERVAL_DAY_TIME), + GI_CONST_BITMAP(SQL_CVT_INTERVAL_YEAR_MONTH), + GI_CONST_BITMAP(SQL_CVT_LONGVARBINARY), + GI_CONST_BITMAP(SQL_CVT_LONGVARCHAR), + GI_CONST_BITMAP(SQL_CVT_NUMERIC), + GI_CONST_BITMAP(SQL_CVT_REAL), + GI_CONST_BITMAP(SQL_CVT_SMALLINT), + GI_CONST_BITMAP(SQL_CVT_TIME), + GI_CONST_BITMAP(SQL_CVT_TIMESTAMP), + GI_CONST_BITMAP(SQL_CVT_TINYINT), + GI_CONST_BITMAP(SQL_CVT_VARBINARY), + GI_CONST_BITMAP(SQL_CVT_VARCHAR), + GI_CONST_BITMAP(SQL_CVT_WCHAR), + GI_CONST_BITMAP(SQL_CVT_WLONGVARCHAR), + GI_CONST_BITMAP(SQL_CVT_WVARCHAR), + GI_CONST_BITMAP(SQL_DA_DROP_ASSERTION), + GI_CONST_BITMAP(SQL_DC_DROP_COLLATION), + GI_CONST_BITMAP(SQL_DCS_DROP_CHARACTER_SET), + GI_CONST_BITMAP(SQL_DD_CASCADE), + GI_CONST_BITMAP(SQL_DD_DROP_DOMAIN), + GI_CONST_BITMAP(SQL_DD_RESTRICT), + GI_CONST_BITMAP(SQL_DI_CREATE_INDEX), + GI_CONST_BITMAP(SQL_DI_DROP_INDEX), + GI_CONST_BITMAP(SQL_DL_SQL92_DATE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_HOUR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_DAY_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR_TO_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_HOUR_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MINUTE), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MINUTE_TO_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_MONTH), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_SECOND), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_YEAR), + GI_CONST_BITMAP(SQL_DL_SQL92_INTERVAL_YEAR_TO_MONTH), + GI_CONST_BITMAP(SQL_DL_SQL92_TIME), + GI_CONST_BITMAP(SQL_DL_SQL92_TIMESTAMP), + GI_CONST_BITMAP(SQL_DS_CASCADE), + GI_CONST_BITMAP(SQL_DS_DROP_SCHEMA), + GI_CONST_BITMAP(SQL_DS_RESTRICT), + GI_CONST_BITMAP(SQL_DT_CASCADE), + GI_CONST_BITMAP(SQL_DTC_ENLIST_EXPENSIVE), + GI_CONST_BITMAP(SQL_DTC_UNENLIST_EXPENSIVE), + GI_CONST_BITMAP(SQL_DT_DROP_TABLE), + GI_CONST_BITMAP(SQL_DTR_DROP_TRANSLATION), + GI_CONST_BITMAP(SQL_DT_RESTRICT), + GI_CONST_BITMAP(SQL_DV_CASCADE), + GI_CONST_BITMAP(SQL_DV_DROP_VIEW), + GI_CONST_BITMAP(SQL_DV_RESTRICT), + GI_CONST_BITMAP(SQL_FD_FETCH_ABSOLUTE), + GI_CONST_BITMAP(SQL_FD_FETCH_BOOKMARK), + GI_CONST_BITMAP(SQL_FD_FETCH_FIRST), + GI_CONST_BITMAP(SQL_FD_FETCH_LAST), + GI_CONST_BITMAP(SQL_FD_FETCH_NEXT), + GI_CONST_BITMAP(SQL_FD_FETCH_PRIOR), + GI_CONST_BITMAP(SQL_FD_FETCH_RELATIVE), +#ifdef SQL_FD_FETCH_RESUME + GI_CONST_BITMAP(SQL_FD_FETCH_RESUME), +#endif + GI_CONST_BITMAP(SQL_FILE_CATALOG), + GI_CONST_BITMAP(SQL_FILE_NOT_SUPPORTED), + GI_CONST_BITMAP(SQL_FILE_QUALIFIER), + GI_CONST_BITMAP(SQL_FILE_TABLE), + GI_CONST_BITMAP(SQL_FN_CVT_CAST), + GI_CONST_BITMAP(SQL_FN_CVT_CONVERT), + GI_CONST_BITMAP(SQL_FN_NUM_ABS), + GI_CONST_BITMAP(SQL_FN_NUM_ACOS), + GI_CONST_BITMAP(SQL_FN_NUM_ASIN), + GI_CONST_BITMAP(SQL_FN_NUM_ATAN), + GI_CONST_BITMAP(SQL_FN_NUM_ATAN2), + GI_CONST_BITMAP(SQL_FN_NUM_CEILING), + GI_CONST_BITMAP(SQL_FN_NUM_COS), + GI_CONST_BITMAP(SQL_FN_NUM_COT), + GI_CONST_BITMAP(SQL_FN_NUM_DEGREES), + GI_CONST_BITMAP(SQL_FN_NUM_EXP), + GI_CONST_BITMAP(SQL_FN_NUM_FLOOR), + GI_CONST_BITMAP(SQL_FN_NUM_LOG), + GI_CONST_BITMAP(SQL_FN_NUM_LOG10), + GI_CONST_BITMAP(SQL_FN_NUM_MOD), + GI_CONST_BITMAP(SQL_FN_NUM_PI), + GI_CONST_BITMAP(SQL_FN_NUM_POWER), + GI_CONST_BITMAP(SQL_FN_NUM_RADIANS), + GI_CONST_BITMAP(SQL_FN_NUM_RAND), + GI_CONST_BITMAP(SQL_FN_NUM_ROUND), + GI_CONST_BITMAP(SQL_FN_NUM_SIGN), + GI_CONST_BITMAP(SQL_FN_NUM_SIN), + GI_CONST_BITMAP(SQL_FN_NUM_SQRT), + GI_CONST_BITMAP(SQL_FN_NUM_TAN), + GI_CONST_BITMAP(SQL_FN_NUM_TRUNCATE), + GI_CONST_BITMAP(SQL_FN_STR_ASCII), + GI_CONST_BITMAP(SQL_FN_STR_BIT_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CHAR), + GI_CONST_BITMAP(SQL_FN_STR_CHARACTER_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CHAR_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_CONCAT), + GI_CONST_BITMAP(SQL_FN_STR_DIFFERENCE), + GI_CONST_BITMAP(SQL_FN_STR_INSERT), + GI_CONST_BITMAP(SQL_FN_STR_LCASE), + GI_CONST_BITMAP(SQL_FN_STR_LEFT), + GI_CONST_BITMAP(SQL_FN_STR_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_LOCATE), + GI_CONST_BITMAP(SQL_FN_STR_LOCATE_2), + GI_CONST_BITMAP(SQL_FN_STR_LTRIM), + GI_CONST_BITMAP(SQL_FN_STR_OCTET_LENGTH), + GI_CONST_BITMAP(SQL_FN_STR_POSITION), + GI_CONST_BITMAP(SQL_FN_STR_REPEAT), + GI_CONST_BITMAP(SQL_FN_STR_REPLACE), + GI_CONST_BITMAP(SQL_FN_STR_RIGHT), + GI_CONST_BITMAP(SQL_FN_STR_RTRIM), + GI_CONST_BITMAP(SQL_FN_STR_SOUNDEX), + GI_CONST_BITMAP(SQL_FN_STR_SPACE), + GI_CONST_BITMAP(SQL_FN_STR_SUBSTRING), + GI_CONST_BITMAP(SQL_FN_STR_UCASE), + GI_CONST_BITMAP(SQL_FN_SYS_DBNAME), + GI_CONST_BITMAP(SQL_FN_SYS_IFNULL), + GI_CONST_BITMAP(SQL_FN_SYS_USERNAME), + GI_CONST_BITMAP(SQL_FN_TD_CURDATE), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_DATE), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_TIME), + GI_CONST_BITMAP(SQL_FN_TD_CURRENT_TIMESTAMP), + GI_CONST_BITMAP(SQL_FN_TD_CURTIME), + GI_CONST_BITMAP(SQL_FN_TD_DAYNAME), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFMONTH), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFWEEK), + GI_CONST_BITMAP(SQL_FN_TD_DAYOFYEAR), + GI_CONST_BITMAP(SQL_FN_TD_EXTRACT), + GI_CONST_BITMAP(SQL_FN_TD_HOUR), + GI_CONST_BITMAP(SQL_FN_TD_MINUTE), + GI_CONST_BITMAP(SQL_FN_TD_MONTH), + GI_CONST_BITMAP(SQL_FN_TD_MONTHNAME), + GI_CONST_BITMAP(SQL_FN_TD_NOW), + GI_CONST_BITMAP(SQL_FN_TD_QUARTER), + GI_CONST_BITMAP(SQL_FN_TD_SECOND), + GI_CONST_BITMAP(SQL_FN_TD_TIMESTAMPADD), + GI_CONST_BITMAP(SQL_FN_TD_TIMESTAMPDIFF), + GI_CONST_BITMAP(SQL_FN_TD_WEEK), + GI_CONST_BITMAP(SQL_FN_TD_YEAR), + GI_CONST_BITMAP(SQL_FN_TSI_DAY), + GI_CONST_BITMAP(SQL_FN_TSI_FRAC_SECOND), + GI_CONST_BITMAP(SQL_FN_TSI_HOUR), + GI_CONST_BITMAP(SQL_FN_TSI_MINUTE), + GI_CONST_BITMAP(SQL_FN_TSI_MONTH), + GI_CONST_BITMAP(SQL_FN_TSI_QUARTER), + GI_CONST_BITMAP(SQL_FN_TSI_SECOND), + GI_CONST_BITMAP(SQL_FN_TSI_WEEK), + GI_CONST_BITMAP(SQL_FN_TSI_YEAR), + GI_CONST_BITMAP(SQL_GB_COLLATE), + GI_CONST_BITMAP(SQL_GB_GROUP_BY_CONTAINS_SELECT), + GI_CONST_BITMAP(SQL_GB_GROUP_BY_EQUALS_SELECT), + GI_CONST_BITMAP(SQL_GB_NO_RELATION), + GI_CONST_BITMAP(SQL_GB_NOT_SUPPORTED), + GI_CONST_BITMAP(SQL_GD_ANY_COLUMN), + GI_CONST_BITMAP(SQL_GD_ANY_ORDER), + GI_CONST_BITMAP(SQL_GD_BLOCK), + GI_CONST_BITMAP(SQL_GD_BOUND), + GI_CONST_BITMAP(SQL_IC_LOWER), + GI_CONST_BITMAP(SQL_IC_MIXED), + GI_CONST_BITMAP(SQL_IC_SENSITIVE), + GI_CONST_BITMAP(SQL_IC_UPPER), + GI_CONST_BITMAP(SQL_IK_ALL), + GI_CONST_BITMAP(SQL_IK_ASC), + GI_CONST_BITMAP(SQL_IK_DESC), + GI_CONST_BITMAP(SQL_IK_NONE), + GI_CONST_BITMAP(SQL_IS_INSERT_LITERALS), + GI_CONST_BITMAP(SQL_IS_INSERT_SEARCHED), + GI_CONST_BITMAP(SQL_IS_SELECT_INTO), + GI_CONST_BITMAP(SQL_ISV_ASSERTIONS), + GI_CONST_BITMAP(SQL_ISV_CHARACTER_SETS), + GI_CONST_BITMAP(SQL_ISV_CHECK_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_COLLATIONS), + GI_CONST_BITMAP(SQL_ISV_COLUMN_DOMAIN_USAGE), + GI_CONST_BITMAP(SQL_ISV_COLUMN_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_COLUMNS), + GI_CONST_BITMAP(SQL_ISV_CONSTRAINT_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_CONSTRAINT_TABLE_USAGE), + GI_CONST_BITMAP(SQL_ISV_DOMAIN_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_DOMAINS), + GI_CONST_BITMAP(SQL_ISV_KEY_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_REFERENTIAL_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_SCHEMATA), + GI_CONST_BITMAP(SQL_ISV_SQL_LANGUAGES), + GI_CONST_BITMAP(SQL_ISV_TABLE_CONSTRAINTS), + GI_CONST_BITMAP(SQL_ISV_TABLE_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_TABLES), + GI_CONST_BITMAP(SQL_ISV_TRANSLATIONS), + GI_CONST_BITMAP(SQL_ISV_USAGE_PRIVILEGES), + GI_CONST_BITMAP(SQL_ISV_VIEW_COLUMN_USAGE), + GI_CONST_BITMAP(SQL_ISV_VIEWS), + GI_CONST_BITMAP(SQL_ISV_VIEW_TABLE_USAGE), + GI_CONST_BITMAP(SQL_LCK_EXCLUSIVE), + GI_CONST_BITMAP(SQL_LCK_NO_CHANGE), + GI_CONST_BITMAP(SQL_LCK_UNLOCK), + GI_CONST_BITMAP(SQL_NC_END), + GI_CONST_BITMAP(SQL_NC_HIGH), + GI_CONST_BITMAP(SQL_NC_LOW), + GI_CONST_BITMAP(SQL_NC_START), + GI_CONST_BITMAP(SQL_NNC_NON_NULL), + GI_CONST_BITMAP(SQL_NNC_NULL), + GI_CONST_BITMAP(SQL_OAC_LEVEL1), + GI_CONST_BITMAP(SQL_OAC_LEVEL2), + GI_CONST_BITMAP(SQL_OAC_NONE), + GI_CONST_BITMAP(SQL_OIC_CORE), + GI_CONST_BITMAP(SQL_OIC_LEVEL1), + GI_CONST_BITMAP(SQL_OIC_LEVEL2), + GI_CONST_BITMAP(SQL_OJ_ALL_COMPARISON_OPS), + GI_CONST_BITMAP(SQL_OJ_FULL), + GI_CONST_BITMAP(SQL_OJ_INNER), + GI_CONST_BITMAP(SQL_OJ_LEFT), + GI_CONST_BITMAP(SQL_OJ_NESTED), + GI_CONST_BITMAP(SQL_OJ_NOT_ORDERED), + GI_CONST_BITMAP(SQL_OJ_RIGHT), + GI_CONST_BITMAP(SQL_OSCC_COMPLIANT), + GI_CONST_BITMAP(SQL_OSCC_NOT_COMPLIANT), + GI_CONST_BITMAP(SQL_OSC_CORE), + GI_CONST_BITMAP(SQL_OSC_EXTENDED), + GI_CONST_BITMAP(SQL_OSC_MINIMUM), + GI_CONST_BITMAP(SQL_OU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_OU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_OU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_OU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_OU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_PARC_BATCH), + GI_CONST_BITMAP(SQL_PARC_NO_BATCH), + GI_CONST_BITMAP(SQL_PAS_BATCH), + GI_CONST_BITMAP(SQL_PAS_NO_BATCH), + GI_CONST_BITMAP(SQL_PAS_NO_SELECT), + GI_CONST_BITMAP(SQL_POS_ADD), + GI_CONST_BITMAP(SQL_POS_DELETE), + GI_CONST_BITMAP(SQL_POS_POSITION), + GI_CONST_BITMAP(SQL_POS_REFRESH), + GI_CONST_BITMAP(SQL_POS_UPDATE), + GI_CONST_BITMAP(SQL_PS_POSITIONED_DELETE), + GI_CONST_BITMAP(SQL_PS_POSITIONED_UPDATE), + GI_CONST_BITMAP(SQL_PS_SELECT_FOR_UPDATE), + GI_CONST_BITMAP(SQL_QL_END), + GI_CONST_BITMAP(SQL_QL_START), + GI_CONST_BITMAP(SQL_QU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_QU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_QU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_QU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_QU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_SCC_ISO92_CLI), + GI_CONST_BITMAP(SQL_SCCO_LOCK), + GI_CONST_BITMAP(SQL_SCCO_OPT_ROWVER), + GI_CONST_BITMAP(SQL_SCCO_OPT_TIMESTAMP), + GI_CONST_BITMAP(SQL_SCCO_OPT_VALUES), + GI_CONST_BITMAP(SQL_SCCO_READ_ONLY), + GI_CONST_BITMAP(SQL_SCC_XOPEN_CLI_VERSION1), + GI_CONST_BITMAP(SQL_SC_FIPS127_2_TRANSITIONAL), + GI_CONST_BITMAP(SQL_SC_NON_UNIQUE), + GI_CONST_BITMAP(SQL_SC_SQL92_ENTRY), + GI_CONST_BITMAP(SQL_SC_SQL92_FULL), + GI_CONST_BITMAP(SQL_SC_SQL92_INTERMEDIATE), + GI_CONST_BITMAP(SQL_SC_TRY_UNIQUE), + GI_CONST_BITMAP(SQL_SC_UNIQUE), + GI_CONST_BITMAP(SQL_SDF_CURRENT_DATE), + GI_CONST_BITMAP(SQL_SDF_CURRENT_TIME), + GI_CONST_BITMAP(SQL_SDF_CURRENT_TIMESTAMP), + GI_CONST_BITMAP(SQL_SFKD_CASCADE), + GI_CONST_BITMAP(SQL_SFKD_NO_ACTION), + GI_CONST_BITMAP(SQL_SFKD_SET_DEFAULT), + GI_CONST_BITMAP(SQL_SFKD_SET_NULL), + GI_CONST_BITMAP(SQL_SFKU_CASCADE), + GI_CONST_BITMAP(SQL_SFKU_NO_ACTION), + GI_CONST_BITMAP(SQL_SFKU_SET_DEFAULT), + GI_CONST_BITMAP(SQL_SFKU_SET_NULL), + GI_CONST_BITMAP(SQL_SG_DELETE_TABLE), + GI_CONST_BITMAP(SQL_SG_INSERT_COLUMN), + GI_CONST_BITMAP(SQL_SG_INSERT_TABLE), + GI_CONST_BITMAP(SQL_SG_REFERENCES_COLUMN), + GI_CONST_BITMAP(SQL_SG_REFERENCES_TABLE), + GI_CONST_BITMAP(SQL_SG_SELECT_TABLE), + GI_CONST_BITMAP(SQL_SG_UPDATE_COLUMN), + GI_CONST_BITMAP(SQL_SG_UPDATE_TABLE), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_CHARACTER_SET), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_COLLATION), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_DOMAIN), + GI_CONST_BITMAP(SQL_SG_USAGE_ON_TRANSLATION), + GI_CONST_BITMAP(SQL_SG_WITH_GRANT_OPTION), + GI_CONST_BITMAP(SQL_SNVF_BIT_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_CHARACTER_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_CHAR_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_EXTRACT), + GI_CONST_BITMAP(SQL_SNVF_OCTET_LENGTH), + GI_CONST_BITMAP(SQL_SNVF_POSITION), + GI_CONST_BITMAP(SQL_SO_DYNAMIC), + GI_CONST_BITMAP(SQL_SO_FORWARD_ONLY), + GI_CONST_BITMAP(SQL_SO_KEYSET_DRIVEN), + GI_CONST_BITMAP(SQL_SO_MIXED), + GI_CONST_BITMAP(SQL_SO_STATIC), + GI_CONST_BITMAP(SQL_SP_BETWEEN), + GI_CONST_BITMAP(SQL_SP_COMPARISON), + GI_CONST_BITMAP(SQL_SP_EXISTS), + GI_CONST_BITMAP(SQL_SP_IN), + GI_CONST_BITMAP(SQL_SP_ISNOTNULL), + GI_CONST_BITMAP(SQL_SP_ISNULL), + GI_CONST_BITMAP(SQL_SP_LIKE), + GI_CONST_BITMAP(SQL_SP_MATCH_FULL), + GI_CONST_BITMAP(SQL_SP_MATCH_PARTIAL), + GI_CONST_BITMAP(SQL_SP_MATCH_UNIQUE_FULL), + GI_CONST_BITMAP(SQL_SP_MATCH_UNIQUE_PARTIAL), + GI_CONST_BITMAP(SQL_SP_OVERLAPS), + GI_CONST_BITMAP(SQL_SP_QUANTIFIED_COMPARISON), + GI_CONST_BITMAP(SQL_SP_UNIQUE), + GI_CONST_BITMAP(SQL_SQ_COMPARISON), + GI_CONST_BITMAP(SQL_SQ_CORRELATED_SUBQUERIES), + GI_CONST_BITMAP(SQL_SQ_EXISTS), + GI_CONST_BITMAP(SQL_SQ_IN), + GI_CONST_BITMAP(SQL_SQ_QUANTIFIED), + GI_CONST_BITMAP(SQL_SR_CASCADE), + GI_CONST_BITMAP(SQL_SR_DELETE_TABLE), + GI_CONST_BITMAP(SQL_SR_GRANT_OPTION_FOR), + GI_CONST_BITMAP(SQL_SR_INSERT_COLUMN), + GI_CONST_BITMAP(SQL_SR_INSERT_TABLE), + GI_CONST_BITMAP(SQL_SRJO_CORRESPONDING_CLAUSE), + GI_CONST_BITMAP(SQL_SRJO_CROSS_JOIN), + GI_CONST_BITMAP(SQL_SRJO_EXCEPT_JOIN), + GI_CONST_BITMAP(SQL_SRJO_FULL_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_INNER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_INTERSECT_JOIN), + GI_CONST_BITMAP(SQL_SRJO_LEFT_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_NATURAL_JOIN), + GI_CONST_BITMAP(SQL_SRJO_RIGHT_OUTER_JOIN), + GI_CONST_BITMAP(SQL_SRJO_UNION_JOIN), + GI_CONST_BITMAP(SQL_SR_REFERENCES_COLUMN), + GI_CONST_BITMAP(SQL_SR_REFERENCES_TABLE), + GI_CONST_BITMAP(SQL_SR_RESTRICT), + GI_CONST_BITMAP(SQL_SR_SELECT_TABLE), + GI_CONST_BITMAP(SQL_SR_UPDATE_COLUMN), + GI_CONST_BITMAP(SQL_SR_UPDATE_TABLE), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_CHARACTER_SET), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_COLLATION), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_DOMAIN), + GI_CONST_BITMAP(SQL_SR_USAGE_ON_TRANSLATION), + GI_CONST_BITMAP(SQL_SRVC_DEFAULT), + GI_CONST_BITMAP(SQL_SRVC_NULL), + GI_CONST_BITMAP(SQL_SRVC_ROW_SUBQUERY), + GI_CONST_BITMAP(SQL_SRVC_VALUE_EXPRESSION), + GI_CONST_BITMAP(SQL_SS_ADDITIONS), + GI_CONST_BITMAP(SQL_SS_DELETIONS), + GI_CONST_BITMAP(SQL_SSF_CONVERT), + GI_CONST_BITMAP(SQL_SSF_LOWER), + GI_CONST_BITMAP(SQL_SSF_SUBSTRING), + GI_CONST_BITMAP(SQL_SSF_TRANSLATE), + GI_CONST_BITMAP(SQL_SSF_TRIM_BOTH), + GI_CONST_BITMAP(SQL_SSF_TRIM_LEADING), + GI_CONST_BITMAP(SQL_SSF_TRIM_TRAILING), + GI_CONST_BITMAP(SQL_SSF_UPPER), + GI_CONST_BITMAP(SQL_SS_UPDATES), + GI_CONST_BITMAP(SQL_SU_DML_STATEMENTS), + GI_CONST_BITMAP(SQL_SU_INDEX_DEFINITION), + GI_CONST_BITMAP(SQL_SU_PRIVILEGE_DEFINITION), + GI_CONST_BITMAP(SQL_SU_PROCEDURE_INVOCATION), + GI_CONST_BITMAP(SQL_SU_TABLE_DEFINITION), + GI_CONST_BITMAP(SQL_SVE_CASE), + GI_CONST_BITMAP(SQL_SVE_CAST), + GI_CONST_BITMAP(SQL_SVE_COALESCE), + GI_CONST_BITMAP(SQL_SVE_NULLIF), + GI_CONST_BITMAP(SQL_TC_ALL), + GI_CONST_BITMAP(SQL_TC_DDL_COMMIT), + GI_CONST_BITMAP(SQL_TC_DDL_IGNORE), + GI_CONST_BITMAP(SQL_TC_DML), + GI_CONST_BITMAP(SQL_TC_NONE), + GI_CONST_BITMAP(SQL_TRANSACTION_READ_COMMITTED), + GI_CONST_BITMAP(SQL_TRANSACTION_READ_UNCOMMITTED), + GI_CONST_BITMAP(SQL_TRANSACTION_REPEATABLE_READ), + GI_CONST_BITMAP(SQL_TRANSACTION_SERIALIZABLE), + GI_CONST_BITMAP(SQL_TXN_READ_COMMITTED), + GI_CONST_BITMAP(SQL_TXN_READ_UNCOMMITTED), + GI_CONST_BITMAP(SQL_TXN_REPEATABLE_READ), + GI_CONST_BITMAP(SQL_TXN_SERIALIZABLE), +#ifdef SQL_TXN_VERSIONING + GI_CONST_BITMAP(SQL_TXN_VERSIONING), +#endif + GI_CONST_BITMAP(SQL_US_UNION), + GI_CONST_BITMAP(SQL_US_UNION_ALL), + GI_CONST_BITMAP(SQL_U_UNION), + GI_CONST_BITMAP(SQL_U_UNION_ALL), + + /* end of table */ + GI_CONST_BITMAP_END +}; + +static VALUE +dbc_getinfo(int argc, VALUE *argv, VALUE self) +{ + DBC *p = get_dbc(self); + VALUE which, vtype, vstr; + SQLRETURN ret; + int i, k, info = -1, maptype = -1, info_found = 0; + SQLUSMALLINT sbuffer; + SQLUINTEGER lbuffer; + SQLSMALLINT len_in, len_out; + char *string = NULL, buffer[513]; + + rb_scan_args(argc, argv, "11", &which, &vtype); + switch (TYPE(which)) { + default: + vstr = rb_any_to_s(which); + string = STR2CSTR(vstr); + goto doString; + case T_STRING: + string = STR2CSTR(which); + doString: + for (i = 0; get_info_map[i].name != NULL; i++) { + if (strcmp(string, get_info_map[i].name) == 0) { + info = get_info_map[i].info; + maptype = get_info_map[i].maptype; + info_found = 2; + break; + } + } + break; + case T_FLOAT: + case T_BIGNUM: + k = (int) NUM2DBL(which); + goto doInt; + case T_FIXNUM: + k = FIX2INT(which); + doInt: + info = k; + info_found = 1; + for (i = 0; get_info_map[i].name != NULL; i++) { + if (k == get_info_map[i].info) { + info = get_info_map[i].info; + maptype = get_info_map[i].maptype; + info_found = 3; + break; + } + } + break; + } + switch (info_found) { + case 0: + rb_raise(Cerror, "%s", + set_err("Invalid info type for ODBC::Connection.get_info", + 0)); + return Qnil; + case 1: + sprintf(buffer, "Unknown info type %d for ODBC::Connection.get_info", + info); + set_err(buffer, 1); + break; + } + if (vtype != Qnil) { + switch (TYPE(vtype)) { + case T_FIXNUM: + maptype = FIX2INT(vtype); + break; + case T_BIGNUM: + case T_FLOAT: + maptype = (int) NUM2DBL(vtype); + break; + default: + rb_raise(rb_eTypeError, "need number for sql_type"); + return Qnil; + } + switch (maptype) { + case SQL_NUMERIC: + case SQL_DECIMAL: + case SQL_INTEGER: + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + case SQL_C_ULONG: +#ifdef SQL_BIGINT + case SQL_BIGINT: +#endif +#ifdef SQL_C_UBIGINT + case SQL_C_UBIGINT: +#endif + maptype = SQL_C_LONG; + break; + case SQL_SMALLINT: + case SQL_TINYINT: + case SQL_C_USHORT: + case SQL_C_UTINYINT: + maptype = SQL_C_SHORT; + break; + default: + maptype = SQL_C_CHAR; + break; + } + } + switch (maptype) { + case SQL_C_SHORT: + len_in = sizeof (sbuffer); + sbuffer = 0; + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) &sbuffer, len_in, &len_out); + break; + case SQL_C_LONG: + len_in = sizeof (lbuffer); + lbuffer = 0; + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) &lbuffer, len_in, &len_out); + break; + default: + case SQL_C_CHAR: + len_in = sizeof (buffer) - 1; + memset(buffer, 0, sizeof (buffer)); + ret = SQLGetInfo(p->hdbc, (SQLUSMALLINT) info, + (SQLPOINTER) buffer, len_in, &len_out); + break; + } + if (!SQL_SUCCEEDED(ret)) { + rb_raise(Cerror, "%s", + get_err(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT)); + } + switch (maptype) { + case SQL_C_SHORT: + return INT2NUM(sbuffer); + case SQL_C_LONG: + return INT2NUM(lbuffer); + default: + case SQL_C_CHAR: + return rb_str_new(buffer, len_out); + } + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Fill column type array for statement. + * + *---------------------------------------------------------------------- + */ + +static COLTYPE * +make_coltypes(SQLHSTMT hstmt, int ncols, char **msgp) +{ + int i; + COLTYPE *ret = NULL; + + for (i = 0; i < ncols; i++) { + SQLUSMALLINT ic = i + 1; + SQLLEN type = SQL_UNKNOWN_TYPE, size = 0; + + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_TYPE, NULL, 0, NULL, + &type), + msgp, "SQLColAttributes(SQL_COLUMN_TYPE)")) { + return ret; + } + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, + NULL, 0, NULL, &size), + msgp, "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)")) { + return ret; + } + } + ret = ALLOC_N(COLTYPE, ncols); + if (ret == NULL) { + if (msgp != NULL) { + *msgp = set_err("Out of memory", 0); + } + return NULL; + } + for (i = 0; i < ncols; i++) { + SQLUSMALLINT ic = i + 1; + SQLLEN type = SQL_UNKNOWN_TYPE, size = 0; + + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_TYPE, + NULL, 0, NULL, &type), + "SQLColAttributes(SQL_COLUMN_TYPE)"); + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, + NULL, 0, NULL, &size), + "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)"); + switch (type) { +#ifdef SQL_BIT + case SQL_BIT: +#endif +#ifdef SQL_TINYINT + case SQL_TINYINT: +#endif + case SQL_SMALLINT: + case SQL_INTEGER: + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + break; + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_REAL: + type = SQL_C_DOUBLE; + size = sizeof (double); + break; + case SQL_DATE: +#ifdef SQL_TYPE_DATE + case SQL_TYPE_DATE: +#endif + type = SQL_C_DATE; + size = sizeof (DATE_STRUCT); + break; + case SQL_TIME: +#ifdef SQL_TYPE_TIME + case SQL_TYPE_TIME: +#endif + type = SQL_C_TIME; + size = sizeof (TIME_STRUCT); + break; + case SQL_TIMESTAMP: +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + type = SQL_C_TIMESTAMP; + size = sizeof (TIMESTAMP_STRUCT); + break; + case SQL_LONGVARBINARY: + type = SQL_C_BINARY; + size = SQL_NO_TOTAL; + break; + case SQL_LONGVARCHAR: +#ifdef UNICODE + case SQL_WLONGVARCHAR: + type = SQL_C_WCHAR; +#else + type = SQL_C_CHAR; +#endif + size = SQL_NO_TOTAL; + break; +#ifdef SQL_BIGINT + case SQL_BIGINT: + if (sizeof (SQLBIGINT) == sizeof (SQLINTEGER)) { + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + } else { + type = SQL_C_SBIGINT; + size = sizeof (SQLBIGINT); + } + break; +#endif +#ifdef SQL_UBIGINT + case SQL_UBIGINT: + if (sizeof (SQLBIGINT) == sizeof (SQLINTEGER)) { + type = SQL_C_LONG; + size = sizeof (SQLINTEGER); + } else { + type = SQL_C_UBIGINT; + size = sizeof (SQLBIGINT); + } + break; +#endif + case SQL_DECIMAL: + case SQL_NUMERIC: + if ((size == 0) || (size > SEGSIZE)) { + size = SQL_NO_TOTAL; + } + if (size != SQL_NO_TOTAL) { + size += 2; /* sign and decimal point */ + size += 1; /* NUL terminator */ + } +#ifdef UNICODE + type = SQL_C_WCHAR; + size *= sizeof (SQLWCHAR); +#else + type = SQL_C_CHAR; +#endif + break; + default: + if ((size == 0) || (size > SEGSIZE)) { + size = SQL_NO_TOTAL; + } +#ifdef UNICODE + type = SQL_C_WCHAR; + if (size != SQL_NO_TOTAL) { + size *= sizeof (SQLWCHAR); + size += sizeof (SQLWCHAR); + } +#else + type = SQL_C_CHAR; + if (size != SQL_NO_TOTAL) { + size += 1; + } +#endif + break; + } + ret[i].type = type; + ret[i].size = size; + } + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * Fill parameter info array for statement. + * + *---------------------------------------------------------------------- + */ + +static PARAMINFO * +make_paraminfo(SQLHSTMT hstmt, int nump, char **msgp) +{ + int i; + PARAMINFO *paraminfo = NULL; + + paraminfo = ALLOC_N(PARAMINFO, nump); + if (paraminfo == NULL) { + if (msgp != NULL) { + *msgp = set_err("Out of memory", 0); + } + return NULL; + } + for (i = 0; i < nump; i++) { + paraminfo[i].iotype = SQL_PARAM_INPUT; + paraminfo[i].outsize = 0; + paraminfo[i].outbuf = NULL; + paraminfo[i].rlen = SQL_NULL_DATA; + paraminfo[i].ctype = SQL_C_CHAR; +#ifdef UNICODE + paraminfo[i].outtype = SQL_WCHAR; +#else + paraminfo[i].outtype = SQL_CHAR; +#endif + paraminfo[i].coldef_max = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLDescribeParam(hstmt, (SQLUSMALLINT) (i + 1), + ¶minfo[i].type, + ¶minfo[i].coldef, + ¶minfo[i].scale, + ¶minfo[i].nullable), + NULL, "SQLDescribeParam")) { +#ifdef UNICODE + paraminfo[i].type = SQL_WVARCHAR; +#else + paraminfo[i].type = SQL_VARCHAR; +#endif + paraminfo[i].coldef = 0; + paraminfo[i].scale = 0; + paraminfo[i].nullable = SQL_NULLABLE_UNKNOWN; + paraminfo[i].override = 0; + } + } + return paraminfo; +} + +static void +retain_paraminfo_override(STMT *q, int nump, PARAMINFO *paraminfo) +{ + if ((q->paraminfo != NULL) && (q->nump == nump)) { + int i; + + for (i = 0; i < nump; i++) { + paraminfo[i].iotype = q->paraminfo[i].iotype; + paraminfo[i].rlen = q->paraminfo[i].rlen; + paraminfo[i].ctype = q->paraminfo[i].ctype; + paraminfo[i].outtype = q->paraminfo[i].outtype; + paraminfo[i].outsize = q->paraminfo[i].outsize; + if (q->paraminfo[i].outbuf != NULL) { + paraminfo[i].outbuf = q->paraminfo[i].outbuf; + q->paraminfo[i].outbuf = NULL; + } + if (q->paraminfo[i].override) { + paraminfo[i].override = q->paraminfo[i].override; + paraminfo[i].type = q->paraminfo[i].type; + paraminfo[i].coldef = q->paraminfo[i].coldef; + paraminfo[i].scale = q->paraminfo[i].scale; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Wrap SQLHSTMT into struct/VALUE. + * + *---------------------------------------------------------------------- + */ + +static VALUE +wrap_stmt(VALUE dbc, DBC *p, SQLHSTMT hstmt, STMT **qp) +{ + VALUE stmt = Qnil; + STMT *q; + int i; + + stmt = Data_Make_Struct(Cstmt, STMT, mark_stmt, free_stmt, q); + tracemsg(2, fprintf(stderr, "ObjAlloc: STMT %p\n", q);); + list_init(&q->link, offsetof(STMT, link)); + q->self = stmt; + q->hstmt = hstmt; + q->dbc = dbc; + q->dbcp = NULL; + q->paraminfo = NULL; + q->coltypes = NULL; + q->colnames = q->dbufs = NULL; + q->colvals = NULL; + q->fetchc = 0; + q->upc = p->upc; + q->usef = 0; + rb_iv_set(q->self, "@_a", rb_ary_new()); + rb_iv_set(q->self, "@_h", rb_hash_new()); + for (i = 0; i < 4; i++) { + rb_iv_set(q->self, colnamebuf[i], rb_hash_new()); + } + if (hstmt != SQL_NULL_HSTMT) { + link_stmt(q, p); + } else { + q->dbc = Qnil; + } + if (qp != NULL) { + *qp = q; + } + return stmt; +} + +/* + *---------------------------------------------------------------------- + * + * Create statement with result. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_result(VALUE dbc, SQLHSTMT hstmt, VALUE result, int mode) +{ + DBC *p; + STMT *q; + SQLSMALLINT cols = 0, nump; + COLTYPE *coltypes = NULL; + PARAMINFO *paraminfo = NULL; + char *msg = NULL; + + Data_Get_Struct(dbc, DBC, p); + if ((hstmt == SQL_NULL_HSTMT) || + !succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLNumParams(hstmt, &nump), NULL, "SQLNumParams")) { + nump = 0; + } + if (nump > 0) { + paraminfo = make_paraminfo(hstmt, nump, &msg); + if (paraminfo == NULL) { + goto error; + } + } + if ((mode & MAKERES_PREPARE) || + (hstmt == SQL_NULL_HSTMT) || + (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLNumResultCols(hstmt, &cols), NULL, + "SQLNumResultCols"))) { + cols = 0; + } + if (cols > 0) { + coltypes = make_coltypes(hstmt, cols, &msg); + if (coltypes == NULL) { + goto error; + } + } + if (result == Qnil) { + result = wrap_stmt(dbc, p, hstmt, &q); + } else { + Data_Get_Struct(result, STMT, q); + retain_paraminfo_override(q, nump, paraminfo); + free_stmt_sub(q, 1); + if (q->dbc != dbc) { + unlink_stmt(q); + q->dbc = dbc; + if (hstmt != SQL_NULL_HSTMT) { + link_stmt(q, p); + } + } + q->hstmt = hstmt; + } + q->nump = nump; + q->paraminfo = paraminfo; + q->ncols = cols; + q->coltypes = coltypes; + if ((mode & MAKERES_BLOCK) && rb_block_given_p()) { + if (mode & MAKERES_NOCLOSE) { + return rb_yield(result); + } + return rb_ensure(rb_yield, result, stmt_close, result); + } + return result; +error: + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + if (result != Qnil) { + Data_Get_Struct(result, STMT, q); + if (q->hstmt == hstmt) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + } + if (paraminfo != NULL) { + xfree(paraminfo); + } + if (coltypes != NULL) { + xfree(coltypes); + } + rb_raise(Cerror, "%s", msg); + return Qnil; +} + +static char * +upcase_if(char *string, int upc) +{ + if (upc && (string != NULL)) { + unsigned char *p = (unsigned char *) string; + + while (*p != '\0') { +#ifdef UNICODE + if ((*p < 0x80) && ISLOWER(*p)) +#else + if (ISLOWER(*p)) +#endif + { + *p = toupper(*p); + } + ++p; + } + } + return string; +} + +/* + *---------------------------------------------------------------------- + * + * Constructor: make column from statement. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_column(SQLHSTMT hstmt, int i, int upc, int use_scn) +{ + VALUE obj, v; + SQLUSMALLINT ic = i + 1; + SQLLEN iv; +#ifdef UNICODE + SQLWCHAR name[SQL_MAX_MESSAGE_LENGTH]; +#else + char name[SQL_MAX_MESSAGE_LENGTH]; +#endif + char *msg; + SQLSMALLINT name_len; + + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + (SQLSMALLINT) sizeof (name), + &name_len, NULL), + &msg, use_scn ? "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)")) { + rb_raise(Cerror, "%s", msg); + } + obj = rb_obj_alloc(Ccolumn); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + if (upc) { + int len = uc_strlen(name); + char tmpbuf[1]; + char *tmp = xmalloc(len); + VALUE v; + + if (tmp == NULL) { + tmp = tmpbuf; + len = 0; + } + mkutf(tmp, name, len); + v = rb_tainted_str_new2(upcase_if(tmp, 1)); +#ifdef USE_RB_ENC + rb_enc_associate(v, rb_enc); +#endif + rb_iv_set(obj, "@name", v); + if ((tmp != NULL) && (tmp != tmpbuf)) { + xfree(tmp); + } + } else { + rb_iv_set(obj, "@name", uc_tainted_str_new2(name)); + } +#else + rb_iv_set(obj, "@name", rb_tainted_str_new2(upcase_if(name, upc))); +#endif + v = Qnil; + name[0] = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_TABLE_NAME, name, + (SQLSMALLINT) sizeof (name), + &name_len, NULL), + NULL, "SQLColAttributes(SQL_COLUMN_TABLE_NAME)")) { + if (name_len > (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + v = uc_tainted_str_new2(name); +#else + v = rb_tainted_str_new2(name); +#endif + } + rb_iv_set(obj, "@table", v); + iv = SQL_UNKNOWN_TYPE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_TYPE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_TYPE)")) { + v = INT2NUM(iv); + } else { + v = INT2NUM(SQL_UNKNOWN_TYPE); + } + rb_iv_set(obj, "@type", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, +#if (ODBCVER >= 0x0300) + SQL_DESC_LENGTH, +#else + SQL_COLUMN_LENGTH, +#endif + NULL, 0, NULL, &iv), + NULL, +#if (ODBCVER >= 0x0300) + "SQLColAttributes(SQL_DESC_LENGTH)" +#else + "SQLColAttributes(SQL_COLUMN_LENGTH)" +#endif + )) { + v = INT2NUM(iv); + } else if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, + SQL_COLUMN_DISPLAY_SIZE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_DISPLAY_SIZE)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@length", v); + v = Qnil; + iv = SQL_NO_NULLS; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_NULLABLE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_NULLABLE)")) { + v = (iv == SQL_NO_NULLS) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@nullable", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_SCALE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_SCALE)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@scale", v); + v = Qnil; + iv = 0; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_PRECISION, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_PRECISION)")) { + v = INT2NUM(iv); + } + rb_iv_set(obj, "@precision", v); + v = Qnil; + iv = SQL_UNSEARCHABLE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_SEARCHABLE, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_SEARCHABLE)")) { + v = (iv == SQL_UNSEARCHABLE) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@searchable", v); + v = Qnil; + iv = SQL_FALSE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_UNSIGNED, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_UNSIGNED)")) { + v = (iv == SQL_FALSE) ? Qfalse : Qtrue; + } + rb_iv_set(obj, "@unsigned", v); + v = Qnil; +#ifdef SQL_COLUMN_AUTO_INCREMENT + iv = SQL_FALSE; + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLColAttributes(hstmt, ic, SQL_COLUMN_AUTO_INCREMENT, NULL, + 0, NULL, &iv), + NULL, "SQLColAttributes(SQL_COLUMN_AUTO_INCREMENT)")) { + v = (iv == SQL_FALSE) ? Qfalse : Qtrue; + } +#endif + rb_iv_set(obj, "@autoincrement", v); + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Constructor: make parameter from statement. + * + *---------------------------------------------------------------------- + */ + +static VALUE +make_param(STMT *q, int i) +{ + VALUE obj; + int v; + + obj = rb_obj_alloc(Cparam); +#ifdef UNICODE + v = q->paraminfo ? q->paraminfo[i].type : SQL_WVARCHAR; +#else + v = q->paraminfo ? q->paraminfo[i].type : SQL_VARCHAR; +#endif + rb_iv_set(obj, "@type", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].coldef : 0; + rb_iv_set(obj, "@precision", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].scale : 0; + rb_iv_set(obj, "@scale", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].nullable : SQL_NULLABLE_UNKNOWN; + rb_iv_set(obj, "@nullable", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].iotype : SQL_PARAM_INPUT; + rb_iv_set(obj, "@iotype", INT2NUM(v)); + v = q->paraminfo ? q->paraminfo[i].outsize : 0; + rb_iv_set(obj, "@output_size", INT2NUM(v)); +#ifdef UNICODE + v = q->paraminfo ? q->paraminfo[i].outtype : SQL_WCHAR; +#else + v = q->paraminfo ? q->paraminfo[i].outtype : SQL_CHAR; +#endif + rb_iv_set(obj, "@output_type", INT2NUM(v)); + return obj; +} + +/* + *---------------------------------------------------------------------- + * + * Query tables/columns/keys/indexes/types of data source. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_info(int argc, VALUE *argv, VALUE self, int mode) +{ + DBC *p = get_dbc(self); + VALUE which = Qnil, which2 = Qnil, which3 = Qnil; +#ifdef UNICODE + SQLWCHAR *swhich = NULL, *swhich2 = NULL; +#else + SQLCHAR *swhich = NULL, *swhich2 = NULL; +#endif + char *msg; + const char *argspec = NULL; + SQLHSTMT hstmt; + int needstr = 1, itype = SQL_ALL_TYPES; + int iid = SQL_BEST_ROWID, iscope = SQL_SCOPE_CURROW; + + if (p->hdbc == SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("No connection", 0)); + } + switch (mode) { + case INFO_TYPES: + needstr = 0; + /* FALL THRU */ + case INFO_TABLES: + case INFO_PRIMKEYS: + case INFO_TPRIV: + case INFO_PROCS: + argspec = "01"; + break; + case INFO_COLUMNS: + argspec = "02"; + break; + case INFO_INDEXES: + case INFO_FORKEYS: + case INFO_PROCCOLS: + argspec = "11"; + break; + case INFO_SPECCOLS: + argspec = "12"; + break; + default: + rb_raise(Cerror, "%s", set_err("Invalid info mode", 0)); + break; + } + rb_scan_args(argc, argv, argspec, &which, &which2, &which3); + if (which != Qnil) { + if (needstr) { + Check_Type(which, T_STRING); +#ifdef UNICODE + swhich = (SQLWCHAR *) which; +#else + swhich = (SQLCHAR *) STR2CSTR(which); +#endif + } else { + itype = NUM2INT(which); + } + } + if (which2 != Qnil) { + if (mode == INFO_SPECCOLS) { + iid = NUM2INT(which2); + } else if (mode != INFO_INDEXES) { + Check_Type(which2, T_STRING); +#ifdef UNICODE + swhich2 = (SQLWCHAR *) which2; +#else + swhich2 = (SQLCHAR *) STR2CSTR(which2); +#endif + } + } +#ifdef UNICODE + if (swhich != NULL) { + VALUE val = (VALUE) swhich; + +#ifdef USE_RB_ENC + val = rb_funcall(val, IDencode, 1, rb_encv); +#endif + swhich = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (swhich == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + } + if (swhich2 != NULL) { + VALUE val = (VALUE) swhich2; + +#ifdef USE_RB_ENC + val = rb_funcall(val, IDencode, 1, rb_encv); +#endif + swhich2 = uc_from_utf((unsigned char *) STR2CSTR(val), -1); + if (swhich2 == NULL) { + uc_free(swhich); + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + } +#endif + if (which3 != Qnil) { + iscope = NUM2INT(which3); + } + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), &msg, "SQLAllocStmt")) { +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + rb_raise(Cerror, "%s", msg); + } + switch (mode) { + case INFO_TABLES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLTABLES(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + NULL, 0), + &msg, "SQLTables")) { + goto error; + } + break; + case INFO_COLUMNS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLCOLUMNS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + swhich2, (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLColumns")) { + goto error; + } + break; + case INFO_PRIMKEYS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPRIMARYKEYS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLPrimaryKeys")) { + goto error; + } + break; + case INFO_INDEXES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLSTATISTICS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + (SQLUSMALLINT) (RTEST(which2) ? + SQL_INDEX_UNIQUE : SQL_INDEX_ALL), + SQL_ENSURE), + &msg, "SQLStatistics")) { + goto error; + } + break; + case INFO_TYPES: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLGETTYPEINFO(hstmt, (SQLSMALLINT) itype), + &msg, "SQLGetTypeInfo")) { + goto error; + } + break; + case INFO_FORKEYS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFOREIGNKEYS(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS, + NULL, 0, NULL, 0, + swhich2, (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLForeignKeys")) { + goto error; + } + break; + case INFO_TPRIV: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLTABLEPRIVILEGES(hstmt, NULL, 0, NULL, 0, swhich, + (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLTablePrivileges")) { + goto error; + } + break; + case INFO_PROCS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPROCEDURES(hstmt, NULL, 0, NULL, 0, + swhich, (swhich == NULL) ? 0 : SQL_NTS), + &msg, "SQLProcedures")) { + goto error; + } + break; + case INFO_PROCCOLS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLPROCEDURECOLUMNS(hstmt, NULL, 0, NULL, 0, + swhich, + (swhich == NULL) ? 0 : SQL_NTS, + swhich2, + (swhich2 == NULL) ? 0 : SQL_NTS), + &msg, "SQLProcedureColumns")) { + goto error; + } + break; + case INFO_SPECCOLS: + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLSPECIALCOLUMNS(hstmt, (SQLUSMALLINT) iid, + NULL, 0, NULL, 0, + swhich, + (swhich == NULL) ? 0 : SQL_NTS, + (SQLUSMALLINT) iscope, + SQL_NULLABLE), + &msg, "SQLSpecialColumns")) { + goto error; + } + break; + } +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + return make_result(self, hstmt, Qnil, MAKERES_BLOCK); +error: +#ifdef UNICODE + uc_free(swhich); + uc_free(swhich2); +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + rb_raise(Cerror, "%s", msg); + return Qnil; +} + +static VALUE +dbc_tables(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TABLES); +} + +static VALUE +dbc_columns(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_COLUMNS); +} + +static VALUE +dbc_primkeys(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PRIMKEYS); +} + +static VALUE +dbc_indexes(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_INDEXES); +} + +static VALUE +dbc_types(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TYPES); +} + +static VALUE +dbc_forkeys(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_FORKEYS); +} + +static VALUE +dbc_tpriv(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_TPRIV); +} + +static VALUE +dbc_procs(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PROCS); +} + +static VALUE +dbc_proccols(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_PROCCOLS); +} + +static VALUE +dbc_speccols(int argc, VALUE *argv, VALUE self) +{ + return dbc_info(argc, argv, self, INFO_SPECCOLS); +} + +/* + *---------------------------------------------------------------------- + * + * Transaction stuff. + * + *---------------------------------------------------------------------- + */ + +static VALUE +dbc_trans(VALUE self, int what) +{ + ENV *e; + SQLHDBC dbc = SQL_NULL_HDBC; + SQLRETURN ret; + char *msg; + + e = get_env(self); + if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *d; + + d = get_dbc(self); + dbc = d->hdbc; + } +#if (ODBCVER >= 0x0300) + ret = SQLENDTRAN((SQLSMALLINT) + ((dbc == SQL_NULL_HDBC) ? SQL_HANDLE_ENV : SQL_HANDLE_DBC), + (dbc == SQL_NULL_HDBC) ? e->henv : dbc, + (SQLSMALLINT) what); +#else + ret = SQLTRANSACT(e->henv, dbc, (SQLUSMALLINT) what); +#endif + if (!succeeded(e->henv, dbc, SQL_NULL_HSTMT, ret, +#if (ODBCVER >= 0x0300) + &msg, "SQLEndTran" +#else + &msg, "SQLTransact" +#endif + )) { + rb_raise(Cerror, "%s", msg); + } + return Qnil; +} + +static VALUE +dbc_commit(VALUE self) +{ + return dbc_trans(self, SQL_COMMIT); +} + +static VALUE +dbc_rollback(VALUE self) +{ + return dbc_trans(self, SQL_ROLLBACK); +} + +static VALUE +dbc_nop(VALUE self) +{ + return Qnil; +} + +static VALUE +dbc_transbody(VALUE self) +{ + return rb_yield(rb_ary_entry(self, 0)); +} + +static VALUE +dbc_transfail(VALUE self, VALUE err) +{ + rb_ary_store(self, 1, err); + dbc_rollback(rb_ary_entry(self, 0)); + return Qundef; +} + +static VALUE +dbc_transaction(VALUE self) +{ + VALUE a, ret; + + if (!rb_block_given_p()) { + rb_raise(rb_eArgError, "block required"); + } + rb_ensure(dbc_commit, self, dbc_nop, self); + a = rb_ary_new2(2); + rb_ary_store(a, 0, self); + rb_ary_store(a, 1, Qnil); + if ((ret = rb_rescue2(dbc_transbody, a, dbc_transfail, a, + rb_eException, (VALUE) 0)) != Qundef) { + dbc_commit(self); + return ret; + } + ret = rb_ary_entry(a, 1); + rb_exc_raise(rb_exc_new3(rb_obj_class(ret), + FUNCALL_NOARGS(ret, IDto_s))); + return Qnil; +} + +/* + *---------------------------------------------------------------------- + * + * Environment attribute handling. + * + *---------------------------------------------------------------------- + */ + +#if (ODBCVER >= 0x0300) +static VALUE +do_attr(int argc, VALUE *argv, VALUE self, int op) +{ + SQLHENV henv = SQL_NULL_HENV; + VALUE val; + SQLLEN v = 0; + SQLPOINTER vp; + SQLINTEGER l; + char *msg; + + if (self != Modbc) { + henv = get_env(self)->henv; + } + rb_scan_args(argc, argv, "01", &val); + if (val == Qnil) { + if (!succeeded(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLGetEnvAttr(henv, (SQLINTEGER) op, + (SQLPOINTER) &v, sizeof (v), &l), + &msg, "SQLGetEnvAttr(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + return rb_int2inum(v); + } + v = NUM2INT(val); + vp = (SQLPOINTER) v; + if (!succeeded(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, + SQLSetEnvAttr(henv, (SQLINTEGER) op, vp, SQL_IS_INTEGER), + &msg, "SQLSetEnvAttr(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + return Qnil; +} +#endif + +static VALUE +env_cpooling(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_CONNECTION_POOLING); +#else + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); + return Qnil; +#endif +} + +static VALUE +env_cpmatch(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_CP_MATCH); +#else + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); + return Qnil; +#endif +} + +static VALUE +env_odbcver(int argc, VALUE *argv, VALUE self) +{ +#if (ODBCVER >= 0x0300) + return do_attr(argc, argv, self, SQL_ATTR_ODBC_VERSION); +#else + VALUE val; + + rb_scan_args(argc, argv, "01", &val); + if (val == Qnil) { + return rb_int2inum(ODBCVER >> 8); + } + rb_raise(Cerror, "%s", set_err("Unsupported in ODBC < 3.0", 0)); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Connection/statement option handling. + * + * Note: + * ODBC 2 allows statement options to be set using SQLSetConnectOption, + * establishing the statement option as a default for any hstmts + * later allocated for that hdbc. This feature was deprecated in + * ODBC 3.x and may not work with ODBC 3.x drivers. + * + * Although the Database class includes attribute accessors for + * statement-level options, a safer alternative, if using an ODBC 3 + * driver, is to set the option directly on the Statement instance. + * + *---------------------------------------------------------------------- + */ + +#define OPT_LEVEL_STMT 1 +#define OPT_LEVEL_DBC 2 +#define OPT_LEVEL_BOTH (OPT_LEVEL_STMT | OPT_LEVEL_DBC) + +#define OPT_CONST_INT(x, level) { #x, x, level } +#define OPT_CONST_END { NULL, -1 } +static struct { + const char *name; + int option; + int level; +} option_map[] = { + + /* yielding ints */ + OPT_CONST_INT(SQL_AUTOCOMMIT, OPT_LEVEL_DBC), + OPT_CONST_INT(SQL_LOGIN_TIMEOUT, OPT_LEVEL_DBC), + OPT_CONST_INT(SQL_NOSCAN, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_CONCURRENCY, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_QUERY_TIMEOUT, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_MAX_ROWS, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_MAX_LENGTH, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_ROWSET_SIZE, OPT_LEVEL_BOTH), + OPT_CONST_INT(SQL_CURSOR_TYPE, OPT_LEVEL_BOTH), + + /* end of table */ + OPT_CONST_END +}; + +static VALUE +do_option(int argc, VALUE *argv, VALUE self, int isstmt, int op) +{ + DBC *p = NULL; + STMT *q = NULL; + VALUE val, val2 = Qnil; + SQLINTEGER v; + char *msg; + int level = isstmt ? OPT_LEVEL_STMT : OPT_LEVEL_DBC; + + if (op == -1) { + rb_scan_args(argc, argv, "11", &val, &val2); + } else { + rb_scan_args(argc, argv, "01", &val); + } + if (isstmt) { + Data_Get_Struct(self, STMT, q); + if (q->dbc == Qnil) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + if (q->hstmt == SQL_NULL_HSTMT) { + rb_raise(Cerror, "%s", set_err("No statement", 0)); + } + } else { + p = get_dbc(self); + if (p->hdbc == SQL_NULL_HDBC) { + rb_raise(Cerror, "%s", set_err("No connection", 0)); + } + } + if (op == -1) { + VALUE vstr; + char *string; + int i, op_found = 0; + + switch (TYPE(val)) { + default: + vstr = rb_any_to_s(val); + string = STR2CSTR(vstr); + goto doString; + case T_STRING: + string = STR2CSTR(val); + doString: + for (i = 0; option_map[i].name != NULL; i++) { + if (strcmp(string, option_map[i].name) == 0) { + op = option_map[i].option; + level = option_map[i].level; + op_found = 3; + break; + } + } + break; + case T_FLOAT: + case T_BIGNUM: + op = (int) NUM2DBL(val); + goto doInt; + case T_FIXNUM: + op = FIX2INT(val); + doInt: + op_found = 1; + for (i = 0; option_map[i].name != NULL; i++) { + if (op == option_map[i].option) { + level = option_map[i].level; + op_found = 2; + break; + } + } + break; + } + if (!op_found) { + rb_raise(Cerror, "%s", set_err("Unknown option", 0)); + return Qnil; + } + val = val2; + } + if ((isstmt && (!(level & OPT_LEVEL_STMT))) || + (!isstmt && (!(level & OPT_LEVEL_DBC)))) { + rb_raise(Cerror, "%s", + set_err("Invalid option type for this level", 0)); + return Qnil; + } + if (val == Qnil) { + if (p != NULL) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLGetConnectOption(p->hdbc, (SQLUSMALLINT) op, + (SQLPOINTER) &v), + &msg, "SQLGetConnectOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } else { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HSTMT, q->hstmt, + SQLGetStmtOption(q->hstmt, (SQLUSMALLINT) op, + (SQLPOINTER) &v), + &msg, "SQLGetStmtOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } + } + switch (op) { + case SQL_AUTOCOMMIT: + if (val == Qnil) { + return v ? Qtrue : Qfalse; + } + v = (TYPE(val) == T_FIXNUM) ? + (FIX2INT(val) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF) : + (RTEST(val) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); + break; + + case SQL_NOSCAN: + if (val == Qnil) { + return v ? Qtrue : Qfalse; + } + v = (TYPE(val) == T_FIXNUM) ? + (FIX2INT(val) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF) : + (RTEST(val) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF); + break; + + case SQL_LOGIN_TIMEOUT: + case SQL_CONCURRENCY: + case SQL_QUERY_TIMEOUT: + case SQL_MAX_ROWS: + case SQL_MAX_LENGTH: + case SQL_ROWSET_SIZE: + case SQL_CURSOR_TYPE: + default: + if (val == Qnil) { + return rb_int2inum(v); + } + Check_Type(val, T_FIXNUM); + v = FIX2INT(val); + if (op == SQL_ROWSET_SIZE) { + rb_raise(Cerror, "%s", set_err("Read only attribute", 0)); + } + break; + } + if (p != NULL) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLSetConnectOption(p->hdbc, (SQLUSMALLINT) op, + (SQLUINTEGER) v), + &msg, "SQLSetConnectOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } else { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLSetStmtOption(q->hstmt, (SQLUSMALLINT) op, + (SQLUINTEGER) v), + &msg, "SQLSetStmtOption(%d)", op)) { + rb_raise(Cerror, "%s", msg); + } + } + return Qnil; +} + +static VALUE +dbc_autocommit(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_AUTOCOMMIT); +} + +static VALUE +dbc_concurrency(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_CONCURRENCY); +} + +static VALUE +dbc_maxrows(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_MAX_ROWS); +} + +static VALUE +dbc_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_QUERY_TIMEOUT); +} + +static VALUE +dbc_login_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_LOGIN_TIMEOUT); +} + +static VALUE +dbc_maxlength(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_MAX_LENGTH); +} + +static VALUE +dbc_rowsetsize(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_ROWSET_SIZE); +} + +static VALUE +dbc_cursortype(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_CURSOR_TYPE); +} + +static VALUE +dbc_noscan(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, SQL_NOSCAN); +} + +static VALUE +dbc_getsetoption(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 0, -1); +} + +static VALUE +stmt_concurrency(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_CONCURRENCY); +} + +static VALUE +stmt_maxrows(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_MAX_ROWS); +} + +static VALUE +stmt_timeout(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_QUERY_TIMEOUT); +} + +static VALUE +stmt_maxlength(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_MAX_LENGTH); +} + +static VALUE +stmt_rowsetsize(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_ROWSET_SIZE); +} + +static VALUE +stmt_cursortype(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_CURSOR_TYPE); +} + +static VALUE +stmt_noscan(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, SQL_NOSCAN); +} + +static VALUE +stmt_getsetoption(int argc, VALUE *argv, VALUE self) +{ + return do_option(argc, argv, self, 1, -1); +} + +/* + *---------------------------------------------------------------------- + * + * Scan literal date/time/timestamp to TIMESTAMP_STRUCT. + * + *---------------------------------------------------------------------- + */ + +static int +scan_dtts(VALUE str, int do_d, int do_t, TIMESTAMP_STRUCT *ts) +{ + int yy = 0, mm = 0, dd = 0, hh = 0, mmm = 0, ss = 0, ff = 0, i; + char c, *cstr = STR2CSTR(str); + + memset(ts, 0, sizeof (TIMESTAMP_STRUCT)); + if (((sscanf(cstr, "{ts '%d-%d-%d %d:%d:%d.%d' %c", + &yy, &mm, &dd, &hh, &mmm, &ss, &ff, &c) == 8) || + (sscanf(cstr, "{ts '%d-%d-%d %d:%d:%d' %c", + &yy, &mm, &dd, &hh, &mmm, &ss, &c) == 7)) && + (c == '}')) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + ts->fraction = ff; + return 1; + } + if (do_d && + (sscanf(cstr, "{d '%d-%d-%d' %c", &yy, &mm, &dd, &c) == 4) && + (c == '}')) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + return 1; + } + if (do_t && + (sscanf(cstr, "{t '%d:%d:%d' %c", &hh, &mmm, &ss, &c) == 4) && + (c == '}')) { + ts->hour = yy; + ts->minute = mmm; + ts->second = ss; + return 1; + } + ff = ss = 0; + i = sscanf(cstr, "%d-%d-%d %d:%d:%d%c%d", + &yy, &mm, &dd, &hh, &mmm, &ss, &c, &ff); + if (i >= 5) { + if ((i > 6) && (c != 0) && (strchr(". \t", c) == NULL)) { + goto next; + } + ts->year = yy; + ts->month = mm; + ts->day = dd; + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + ts->fraction = ff; + return 1; + } +next: + ff = ss = 0; + if (do_d && (sscanf(cstr, "%d-%d-%d", &yy, &mm, &dd) == 3)) { + ts->year = yy; + ts->month = mm; + ts->day = dd; + return 1; + } + if (do_t && (sscanf(cstr, "%d:%d:%d", &hh, &mmm, &ss) == 3)) { + ts->hour = hh; + ts->minute = mmm; + ts->second = ss; + return 1; + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * Date methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +date_alloc(VALUE self) +{ + DATE_STRUCT *date; + VALUE obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + + memset(date, 0, sizeof (*date)); + return obj; +} +#else +static VALUE +date_new(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +date_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, 1, 0, &tss)) { + DATE_STRUCT *date; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, DATE_STRUCT, 0, xfree, date); + } else { + obj = self; + Data_Get_Struct(self, DATE_STRUCT, date); + } + date->year = tss.year; + date->month = tss.month; + date->day = tss.day; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::Date format error"); + } + return Qnil; +} + +static VALUE +date_load(VALUE self, VALUE str) +{ + return date_load1(self, str, 1); +} + +static VALUE +date_init(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE d, m, y; + + rb_scan_args(argc, argv, "03", &y, &m, &d); + if (rb_obj_is_kind_of(y, Cdate) == Qtrue) { + DATE_STRUCT *date2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, DATE_STRUCT, date); + Data_Get_Struct(y, DATE_STRUCT, date2); + *date = *date2; + return self; + } + if (rb_obj_is_kind_of(y, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, DATE_STRUCT, date); + Data_Get_Struct(y, TIMESTAMP_STRUCT, ts); + date->year = ts->year; + date->month = ts->month; + date->day = ts->day; + return self; + } + if (rb_obj_is_kind_of(y, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + d = FUNCALL_NOARGS(y, IDday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if (rb_obj_is_kind_of(y, rb_cDate) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + d = FUNCALL_NOARGS(y, IDmday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if ((argc == 1) && (rb_obj_is_kind_of(y, rb_cString) == Qtrue)) { + if (date_load1(self, y, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, DATE_STRUCT, date); + date->year = (y == Qnil) ? 0 : NUM2INT(y); + date->month = (m == Qnil) ? 0 : NUM2INT(m); + date->day = (d == Qnil) ? 0 : NUM2INT(d); + return self; +} + +static VALUE +date_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + DATE_STRUCT *date1, *date2; + + Data_Get_Struct(self, DATE_STRUCT, date1); + Data_Get_Struct(obj, DATE_STRUCT, date2); + *date2 = *date1; + return obj; +#else + return date_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +date_to_s(VALUE self) +{ + DATE_STRUCT *date; + char buf[128]; + + Data_Get_Struct(self, DATE_STRUCT, date); + sprintf(buf, "%04d-%02d-%02d", date->year, date->month, date->day); + return rb_str_new2(buf); +} + +static VALUE +date_dump(VALUE self, VALUE depth) +{ + return date_to_s(self); +} + +static VALUE +date_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +date_year(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->year); + } + date->year = NUM2INT(v); + return self; +} + +static VALUE +date_month(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->month); + } + date->month = NUM2INT(v); + return self; +} + +static VALUE +date_day(int argc, VALUE *argv, VALUE self) +{ + DATE_STRUCT *date; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, DATE_STRUCT, date); + if (v == Qnil) { + return INT2NUM(date->day); + } + date->day = NUM2INT(v); + return self; +} + +static VALUE +date_cmp(VALUE self, VALUE date) +{ + DATE_STRUCT *date1, *date2; + + if (rb_obj_is_kind_of(date, Cdate) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Date as argument"); + } + Data_Get_Struct(self, DATE_STRUCT, date1); + Data_Get_Struct(date, DATE_STRUCT, date2); + if (date1->year < date2->year) { + return INT2FIX(-1); + } + if (date1->year == date2->year) { + if (date1->month < date2->month) { + return INT2FIX(-1); + } + if (date1->month == date2->month) { + if (date1->day < date2->day) { + return INT2FIX(-1); + } + if (date1->day == date2->day) { + return INT2FIX(0); + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * Time methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +time_alloc(VALUE self) +{ + TIME_STRUCT *time; + VALUE obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + + memset(time, 0, sizeof (*time)); + return obj; +} +#else +static VALUE +time_new(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +time_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, 0, 1, &tss)) { + TIME_STRUCT *time; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, TIME_STRUCT, 0, xfree, time); + } else { + obj = self; + Data_Get_Struct(self, TIME_STRUCT, time); + } + time->hour = tss.hour; + time->minute = tss.minute; + time->second = tss.second; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::Time format error"); + } + return Qnil; +} + +static VALUE +time_load(VALUE self, VALUE str) +{ + return time_load1(self, str, 1); +} + +static VALUE +time_init(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE h, m, s; + + rb_scan_args(argc, argv, "03", &h, &m, &s); + if (rb_obj_is_kind_of(h, Ctime) == Qtrue) { + TIME_STRUCT *time2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIME_STRUCT, time); + Data_Get_Struct(h, TIME_STRUCT, time2); + *time = *time2; + return self; + } + if (rb_obj_is_kind_of(h, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIME_STRUCT, time); + Data_Get_Struct(h, TIMESTAMP_STRUCT, ts); + time->hour = ts->hour; + time->minute = ts->minute; + time->second = ts->second; + return self; + } + if (rb_obj_is_kind_of(h, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + s = FUNCALL_NOARGS(h, IDsec); + m = FUNCALL_NOARGS(h, IDmin); + h = FUNCALL_NOARGS(h, IDhour); + } else if ((argc == 1) && (rb_obj_is_kind_of(h, rb_cString) == Qtrue)) { + if (time_load1(self, h, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, TIME_STRUCT, time); + time->hour = (h == Qnil) ? 0 : NUM2INT(h); + time->minute = (m == Qnil) ? 0 : NUM2INT(m); + time->second = (s == Qnil) ? 0 : NUM2INT(s); + return self; +} + +static VALUE +time_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + TIME_STRUCT *time1, *time2; + + Data_Get_Struct(self, TIME_STRUCT, time1); + Data_Get_Struct(obj, TIME_STRUCT, time2); + *time2 = *time1; + return obj; +#else + return time_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +time_to_s(VALUE self) +{ + TIME_STRUCT *time; + char buf[128]; + + Data_Get_Struct(self, TIME_STRUCT, time); + sprintf(buf, "%02d:%02d:%02d", time->hour, time->minute, time->second); + return rb_str_new2(buf); +} + +static VALUE +time_dump(VALUE self, VALUE depth) +{ + return time_to_s(self); +} + +static VALUE +time_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +time_hour(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->hour); + } + time->hour = NUM2INT(v); + return self; +} + +static VALUE +time_min(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->minute); + } + time->minute = NUM2INT(v); + return self; +} + +static VALUE +time_sec(int argc, VALUE *argv, VALUE self) +{ + TIME_STRUCT *time; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIME_STRUCT, time); + if (v == Qnil) { + return INT2NUM(time->second); + } + time->second = NUM2INT(v); + return self; +} + +static VALUE +time_cmp(VALUE self, VALUE time) +{ + TIME_STRUCT *time1, *time2; + + if (rb_obj_is_kind_of(time, Ctime) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Time as argument"); + } + Data_Get_Struct(self, TIME_STRUCT, time1); + Data_Get_Struct(time, TIME_STRUCT, time2); + if (time1->hour < time2->hour) { + return INT2FIX(-1); + } + if (time1->hour == time2->hour) { + if (time1->minute < time2->minute) { + return INT2FIX(-1); + } + if (time1->minute == time2->minute) { + if (time1->second < time2->second) { + return INT2FIX(-1); + } + if (time1->second == time2->second) { + return INT2FIX(0); + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * TimeStamp methods. + * + *---------------------------------------------------------------------- + */ + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC +static VALUE +timestamp_alloc(VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + + memset(ts, 0, sizeof (*ts)); + return obj; +} +#else +static VALUE +timestamp_new(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + + rb_obj_call_init(obj, argc, argv); + return obj; +} +#endif + +static VALUE +timestamp_load1(VALUE self, VALUE str, int load) +{ + TIMESTAMP_STRUCT tss; + + if (scan_dtts(str, !load, !load, &tss)) { + TIMESTAMP_STRUCT *ts; + VALUE obj; + + if (load) { + obj = Data_Make_Struct(self, TIMESTAMP_STRUCT, 0, xfree, ts); + } else { + obj = self; + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + } + *ts = tss; + return obj; + } + if (load > 0) { + rb_raise(rb_eTypeError, "marshaled ODBC::TimeStamp format error"); + } + return Qnil; +} + +static VALUE +timestamp_load(VALUE self, VALUE str) +{ + return timestamp_load1(self, str, 1); +} + +static VALUE +timestamp_init(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE d, m, y, hh, mm, ss, f; + + rb_scan_args(argc, argv, "07", &y, &m, &d, &hh, &mm, &ss, &f); + if (rb_obj_is_kind_of(y, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts2; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(y, TIMESTAMP_STRUCT, ts2); + *ts = *ts2; + return self; + } + if (rb_obj_is_kind_of(y, Cdate) == Qtrue) { + DATE_STRUCT *date; + + if (argc > 1) { + if (argc > 2) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + if (rb_obj_is_kind_of(m, Ctime) == Qtrue) { + TIME_STRUCT *time; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(m, TIME_STRUCT, time); + ts->hour = time->hour; + ts->minute = time->minute; + ts->second = time->second; + } else { + rb_raise(rb_eArgError, "need ODBC::Time argument"); + } + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + Data_Get_Struct(y, DATE_STRUCT, date); + ts->year = date->year; + ts->year = date->year; + ts->year = date->year; + ts->fraction = 0; + return self; + } + if (rb_obj_is_kind_of(y, rb_cTime) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + f = FUNCALL_NOARGS(y, IDusec); + ss = FUNCALL_NOARGS(y, IDsec); + mm = FUNCALL_NOARGS(y, IDmin); + hh = FUNCALL_NOARGS(y, IDhour); + d = FUNCALL_NOARGS(y, IDday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + f = INT2NUM(NUM2INT(f) * 1000); + } else if (rb_obj_is_kind_of(y, rb_cDate) == Qtrue) { + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + f = INT2FIX(0); + ss = INT2FIX(0); + mm = INT2FIX(0); + hh = INT2FIX(0); + d = FUNCALL_NOARGS(y, IDmday); + m = FUNCALL_NOARGS(y, IDmonth); + y = FUNCALL_NOARGS(y, IDyear); + } else if ((argc == 1) && (rb_obj_is_kind_of(y, rb_cString) == Qtrue)) { + if (timestamp_load1(self, y, 0) != Qnil) { + return self; + } + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + ts->year = (y == Qnil) ? 0 : NUM2INT(y); + ts->month = (m == Qnil) ? 0 : NUM2INT(m); + ts->day = (d == Qnil) ? 0 : NUM2INT(d); + ts->hour = (hh == Qnil) ? 0 : NUM2INT(hh); + ts->minute = (mm == Qnil) ? 0 : NUM2INT(mm); + ts->second = (ss == Qnil) ? 0 : NUM2INT(ss); + ts->fraction = (f == Qnil) ? 0 : NUM2INT(f); + return self; +} + +static VALUE +timestamp_clone(VALUE self) +{ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + VALUE obj = rb_obj_alloc(CLASS_OF(self)); + TIMESTAMP_STRUCT *ts1, *ts2; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts1); + Data_Get_Struct(obj, TIMESTAMP_STRUCT, ts2); + *ts2 = *ts1; + return obj; +#else + return timestamp_new(1, &self, CLASS_OF(self)); +#endif +} + +static VALUE +timestamp_to_s(VALUE self) +{ + TIMESTAMP_STRUCT *ts; + char buf[256]; + + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d %u", + ts->year, ts->month, ts->day, + ts->hour, ts->minute, ts->second, + (unsigned int) ts->fraction); + return rb_str_new2(buf); +} + +static VALUE +timestamp_dump(VALUE self, VALUE depth) +{ + return timestamp_to_s(self); +} + +static VALUE +timestamp_inspect(VALUE self) +{ + VALUE s = rb_str_new2("#")); +} + +static VALUE +timestamp_year(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->year); + } + ts->year = NUM2INT(v); + return self; +} + +static VALUE +timestamp_month(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->month); + } + ts->month = NUM2INT(v); + return self; +} + +static VALUE +timestamp_day(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->day); + } + ts->day = NUM2INT(v); + return self; +} + +static VALUE +timestamp_hour(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->hour); + } + ts->hour = NUM2INT(v); + return self; +} + +static VALUE +timestamp_min(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->minute); + } + ts->minute = NUM2INT(v); + return self; +} + +static VALUE +timestamp_sec(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->second); + } + ts->second = NUM2INT(v); + return self; +} + +static VALUE +timestamp_fraction(int argc, VALUE *argv, VALUE self) +{ + TIMESTAMP_STRUCT *ts; + VALUE v; + + rb_scan_args(argc, argv, "01", &v); + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts); + if (v == Qnil) { + return INT2NUM(ts->fraction); + } + ts->fraction = NUM2INT(v); + return self; +} + +static VALUE +timestamp_cmp(VALUE self, VALUE timestamp) +{ + TIMESTAMP_STRUCT *ts1, *ts2; + + if (rb_obj_is_kind_of(timestamp, Ctimestamp) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::TimeStamp as argument"); + } + Data_Get_Struct(self, TIMESTAMP_STRUCT, ts1); + Data_Get_Struct(timestamp, TIMESTAMP_STRUCT, ts2); + if (ts1->year < ts2->year) { + return INT2FIX(-1); + } + if (ts1->year == ts2->year) { + if (ts1->month < ts2->month) { + return INT2FIX(-1); + } + if (ts1->month == ts2->month) { + if (ts1->day < ts2->day) { + return INT2FIX(-1); + } + if (ts1->day == ts2->day) { + if (ts1->hour < ts2->hour) { + return INT2FIX(-1); + } + if (ts1->hour == ts2->hour) { + if (ts1->minute < ts2->minute) { + return INT2FIX(-1); + } + if (ts1->minute == ts2->minute) { + if (ts1->second < ts2->second) { + return INT2FIX(-1); + } + if (ts1->second == ts2->second) { + if (ts1->fraction < ts2->fraction) { + return INT2FIX(-1); + } + if (ts1->fraction == ts2->fraction) { + return INT2FIX(0); + } + } + } + } + } + } + } + return INT2FIX(1); +} + +/* + *---------------------------------------------------------------------- + * + * Statement methods. + * + *---------------------------------------------------------------------- + */ + +static VALUE +stmt_drop(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + free_stmt_sub(q, 1); + return self; +} + +static VALUE +stmt_close(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), "SQLFreeStmt(SQL_CLOSE)"); + } + free_stmt_sub(q, 1); + return self; +} + +static VALUE +stmt_cancel(VALUE self) +{ + STMT *q; + char *msg; + + Data_Get_Struct(self, STMT, q); + if (q->hstmt != SQL_NULL_HSTMT) { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLCancel(q->hstmt), &msg, "SQLCancel")) { + rb_raise(Cerror, "%s", msg); + } + } + return self; +} + +static void +check_ncols(STMT *q) +{ + if ((q->hstmt != SQL_NULL_HSTMT) && (q->ncols <= 0) && + (q->coltypes == NULL)) { + COLTYPE *coltypes = NULL; + SQLSMALLINT cols = 0; + + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLNumResultCols(q->hstmt, &cols), NULL, + "SQLNumResultCols") + && (cols > 0)) { + coltypes = make_coltypes(q->hstmt, cols, NULL); + if (coltypes != NULL) { + q->ncols = cols; + q->coltypes = coltypes; + } + } + } +} + +static VALUE +stmt_ncols(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + check_ncols(q); + return INT2FIX(q->ncols); +} + +static VALUE +stmt_nrows(VALUE self) +{ + STMT *q; + SQLLEN rows = -1; + char *msg; + + Data_Get_Struct(self, STMT, q); + if ((q->hstmt != SQL_NULL_HSTMT) && + (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLRowCount(q->hstmt, &rows), &msg, "SQLRowCount"))) { + rb_raise(Cerror, "%s", msg); + } + return INT2NUM(rows); +} + +static VALUE +stmt_nparams(VALUE self) +{ + STMT *q; + + Data_Get_Struct(self, STMT, q); + return INT2FIX(q->nump); +} + +static int +param_num_check(STMT *q, VALUE pnum, int mkparaminfo, int needout) +{ + int vnum; + + Check_Type(pnum, T_FIXNUM); + vnum = NUM2INT(pnum); + if (mkparaminfo && (q->paraminfo == NULL)) { + char *msg = NULL; + SQLSMALLINT nump = 0; + + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLNumParams(q->hstmt, &nump), NULL, "SQLNumParams")) { + nump = 0; + } + if (nump > 0) { + PARAMINFO *paraminfo = make_paraminfo(q->hstmt, nump, &msg); + + if (paraminfo == NULL) { + rb_raise(Cerror, "%s", msg); + } + q->paraminfo = paraminfo; + if (q->paraminfo != NULL) { + q->nump = nump; + } + } + } + if ((q->paraminfo == NULL) || (vnum < 0) || (vnum >= q->nump)) { + rb_raise(rb_eArgError, "parameter number out of bounds"); + } + if (needout) { + if ((q->paraminfo[vnum].iotype != SQL_PARAM_OUTPUT) && + (q->paraminfo[vnum].iotype != SQL_PARAM_INPUT_OUTPUT)) { + rb_raise(Cerror, "not an output parameter"); + } + } + return vnum; +} + +static VALUE +stmt_param_type(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, ptype, pcoldef, pscale; + int vnum; + STMT *q; + + rb_scan_args(argc, argv, "13", &pnum, &ptype, &pcoldef, &pscale); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 1, 0); + if (argc > 1) { + int vtype, vcoldef, vscale; + + Check_Type(ptype, T_FIXNUM); + vtype = NUM2INT(ptype); + if (argc > 2) { + Check_Type(pcoldef, T_FIXNUM); + vcoldef = NUM2INT(pcoldef); + if (argc > 3) { + Check_Type(pscale, T_FIXNUM); + vscale = NUM2INT(pscale); + q->paraminfo[vnum].scale = vscale; + } + q->paraminfo[vnum].coldef = vcoldef; + } + q->paraminfo[vnum].type = vtype; + q->paraminfo[vnum].override = 1; + return Qnil; + } + return INT2NUM(q->paraminfo[vnum].type); +} + +static VALUE +stmt_param_iotype(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, piotype; + int vnum, viotype; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &piotype); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 1, 0); + if (argc > 1) { + Check_Type(piotype, T_FIXNUM); + viotype = NUM2INT(piotype); + switch (viotype) { + case SQL_PARAM_INPUT: + case SQL_PARAM_INPUT_OUTPUT: + case SQL_PARAM_OUTPUT: + q->paraminfo[vnum].iotype = viotype; + break; + } + } + return INT2NUM(q->paraminfo[vnum].iotype); +} + +static VALUE +stmt_param_output_value(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, v; + int vnum; + STMT *q; + + rb_scan_args(argc, argv, "10", &pnum); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + v = Qnil; + if (q->paraminfo[vnum].rlen == SQL_NULL_DATA) { + return v; + } + if (q->paraminfo[vnum].outbuf == NULL) { + rb_raise(Cerror, "no output value available"); + } + switch (q->paraminfo[vnum].ctype) { + case SQL_C_LONG: + v = INT2NUM(*((SQLINTEGER *) q->paraminfo[vnum].outbuf)); + break; + case SQL_C_DOUBLE: + v = rb_float_new(*((double *) q->paraminfo[vnum].outbuf)); + break; + case SQL_C_DATE: + { + DATE_STRUCT *date; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + const char *p; + char buffer[128]; + VALUE d; + + date = (DATE_STRUCT *) q->paraminfo[vnum].outbuf; + p = (q->dbcp->gmtime == Qtrue) ? "+00:00" : ""; + sprintf(buffer, "%d-%d-%dT00:00:00%s", + date->year, date->month, date->day, p); + d = rb_str_new2(buffer); + v = rb_funcall(rb_cDate, IDparse, 1, d); + } else { + v = Data_Make_Struct(Cdate, DATE_STRUCT, 0, xfree, date); + *date = *((DATE_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; + case SQL_C_TIME: + { + TIME_STRUCT *time; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE now, frac; + + time = (TIME_STRUCT *) q->paraminfo[vnum].outbuf; + frac = rb_float_new(0.0); + now = FUNCALL_NOARGS(rb_cTime, IDnow); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? IDutc : IDlocal, + 7, + FUNCALL_NOARGS(now, IDyear), + FUNCALL_NOARGS(now, IDmonth), + FUNCALL_NOARGS(now, IDday), + INT2NUM(time->hour), + INT2NUM(time->minute), + INT2NUM(time->second), + frac); + } else { + v = Data_Make_Struct(Ctime, TIME_STRUCT, 0, xfree, time); + *time = *((TIME_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; + case SQL_C_TIMESTAMP: + { + TIMESTAMP_STRUCT *ts; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE frac; + + ts = (TIMESTAMP_STRUCT *) q->paraminfo[vnum].outbuf; + frac = rb_float_new((double) 1.0e-3 * ts->fraction); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? IDutc : IDlocal, + 7, + INT2NUM(ts->year), + INT2NUM(ts->month), + INT2NUM(ts->day), + INT2NUM(ts->hour), + INT2NUM(ts->minute), + INT2NUM(ts->second), + frac); + } else { + v = Data_Make_Struct(Ctimestamp, TIMESTAMP_STRUCT, + 0, xfree, ts); + *ts = *((TIMESTAMP_STRUCT *) q->paraminfo[vnum].outbuf); + } + } + break; +#ifdef UNICODE + case SQL_C_WCHAR: + v = uc_tainted_str_new((SQLWCHAR *) q->paraminfo[vnum].outbuf, + q->paraminfo[vnum].rlen / sizeof (SQLWCHAR)); + break; +#endif + case SQL_C_CHAR: + v = rb_tainted_str_new(q->paraminfo[vnum].outbuf, + q->paraminfo[vnum].rlen); + break; + } + return v; +} + +static VALUE +stmt_param_output_size(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, psize; + int vnum, vsize; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &psize); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + if (argc > 1) { + Check_Type(psize, T_FIXNUM); + vsize = NUM2INT(psize); + if ((vsize > 0) && (vsize < (int) (4 * sizeof (double)))) { + vsize = 4 * sizeof (double); + } + q->paraminfo[vnum].outsize = (vsize > 0) ? vsize : 0; + } + return INT2NUM(q->paraminfo[vnum].outsize); +} + +static VALUE +stmt_param_output_type(int argc, VALUE *argv, VALUE self) +{ + VALUE pnum, ptype; + int vnum, vtype; + STMT *q; + + rb_scan_args(argc, argv, "11", &pnum, &ptype); + Data_Get_Struct(self, STMT, q); + vnum = param_num_check(q, pnum, 0, 1); + if (argc > 1) { + Check_Type(ptype, T_FIXNUM); + vtype = NUM2INT(ptype); + q->paraminfo[vnum].outtype = vtype; + } + return INT2NUM(q->paraminfo[vnum].outtype); +} + +static VALUE +stmt_cursorname(int argc, VALUE *argv, VALUE self) +{ + VALUE cn = Qnil; + STMT *q; +#ifdef UNICODE + SQLWCHAR cname[SQL_MAX_MESSAGE_LENGTH]; + SQLWCHAR *cp; +#else + SQLCHAR cname[SQL_MAX_MESSAGE_LENGTH]; + SQLCHAR *cp; +#endif + char *msg; + SQLSMALLINT cnLen = 0; + + rb_scan_args(argc, argv, "01", &cn); + Data_Get_Struct(self, STMT, q); + if (cn == Qnil) { + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLGetCursorName(q->hstmt, (SQLTCHAR *) cname, + (SQLSMALLINT) sizeof (cname), &cnLen), + &msg, "SQLGetCursorName")) { + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + cnLen = (cnLen == 0) ? (SQLSMALLINT) uc_strlen(cname) : + (SQLSMALLINT) (cnLen / sizeof (SQLWCHAR)); + return uc_tainted_str_new(cname, cnLen); +#else + cnLen = (cnLen == 0) ? (SQLSMALLINT) strlen((char *) cname) : cnLen; + return rb_tainted_str_new((char *) cname, cnLen); +#endif + } + if (TYPE(cn) != T_STRING) { + cn = rb_any_to_s(cn); + } +#ifdef UNICODE +#ifdef USE_RB_ENC + cn = rb_funcall(cn, IDencode, 1, rb_encv); +#endif + cp = uc_from_utf((unsigned char *) STR2CSTR(cn), -1); + if (cp == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + cp = (SQLCHAR *) STR2CSTR(cn); +#endif + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLSetCursorName(q->hstmt, cp, SQL_NTS), + &msg, "SQLSetCursorName")) { +#ifdef UNICODE + uc_free(cp); +#endif + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + uc_free(cp); +#endif + return cn; +} + +static VALUE +stmt_column(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + VALUE col; + int use_scn = 0; + + rb_scan_args(argc, argv, "1", &col); + Check_Type(col, T_FIXNUM); + Data_Get_Struct(self, STMT, q); + check_ncols(q); + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + return make_column(q->hstmt, FIX2INT(col), q->upc, use_scn); +} + +static VALUE +stmt_columns(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + int i, use_scn = 0; + VALUE res, as_ary = Qfalse; + + rb_scan_args(argc, argv, "01", &as_ary); + Data_Get_Struct(self, STMT, q); + check_ncols(q); + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + if (rb_block_given_p()) { + for (i = 0; i < q->ncols; i++) { + rb_yield(make_column(q->hstmt, i, q->upc, use_scn)); + } + return self; + } + if (RTEST(as_ary)) { + res = rb_ary_new2(q->ncols); + } else { + res = rb_hash_new(); + } + for (i = 0; i < q->ncols; i++) { + VALUE obj; + + obj = make_column(q->hstmt, i, q->upc, use_scn); + if (RTEST(as_ary)) { + rb_ary_store(res, i, obj); + } else { + VALUE name = rb_iv_get(obj, "@name"); + + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char buf[32]; + + sprintf(buf, "#%d", i); + name = rb_str_dup(name); + name = rb_obj_taint(rb_str_cat2(name, buf)); + } + rb_hash_aset(res, name, obj); + } + } + return res; +} + +static VALUE +stmt_param(int argc, VALUE *argv, VALUE self) +{ + STMT *q; + VALUE par; + int i; + + rb_scan_args(argc, argv, "1", &par); + Check_Type(par, T_FIXNUM); + Data_Get_Struct(self, STMT, q); + i = FIX2INT(par); + if ((i < 0) || (i >= q->nump)) { + rb_raise(Cerror, "%s", set_err("Parameter out of bounds", 0)); + } + return make_param(q, i); +} + +static VALUE +stmt_params(VALUE self) +{ + STMT *q; + int i; + VALUE res; + + Data_Get_Struct(self, STMT, q); + if (rb_block_given_p()) { + for (i = 0; i < q->nump; i++) { + rb_yield(make_param(q, i)); + } + return self; + } + res = rb_ary_new2(q->nump); + for (i = 0; i < q->nump; i++) { + VALUE obj; + + obj = make_param(q, i); + rb_ary_store(res, i, obj); + } + return res; +} + +static VALUE +do_fetch(STMT *q, int mode) +{ + int i, use_scn = 0, offc; + char **bufs, *msg; + VALUE res; + + if (q->ncols <= 0) { + rb_raise(Cerror, "%s", set_err("No columns in result set", 0)); + } + if (++q->fetchc >= 500) { + q->fetchc = 0; + start_gc(); + } + bufs = q->dbufs; + if (bufs == NULL) { + int need = sizeof (char *) * q->ncols, needp; + char *p; + + need = LEN_ALIGN(need); + needp = need; + for (i = 0; i < q->ncols; i++) { + if (q->coltypes[i].size != SQL_NO_TOTAL) { + need += LEN_ALIGN(q->coltypes[i].size); + } + } + p = ALLOC_N(char, need); + if (p == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + q->dbufs = bufs = (char **) p; + p += needp; + for (i = 0; i < q->ncols; i++) { + int len = q->coltypes[i].size; + + if (len == SQL_NO_TOTAL) { + bufs[i] = NULL; + } else { + bufs[i] = p; + p += LEN_ALIGN(len); + } + } + } + if (q->dbcp != NULL && q->dbcp->use_sql_column_name == Qtrue) { + use_scn = 1; + } + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASH: + case DOFETCH_HASH2: + case DOFETCH_HASHK: + case DOFETCH_HASHK2: + if (q->colnames == NULL) { + int need = sizeof (char *) * 4 * q->ncols + sizeof (char *); + int max_len[2] = { 0, 0 }; + char **na, *p; +#ifdef UNICODE + SQLWCHAR name[SQL_MAX_MESSAGE_LENGTH]; +#else + char name[SQL_MAX_MESSAGE_LENGTH]; +#endif + SQLSMALLINT name_len; + + for (i = 0; i < q->ncols; i++) { + int need_len; + + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, + (SQLUSMALLINT) (i + 1), + SQL_COLUMN_TABLE_NAME, + name, + sizeof (name), + &name_len, NULL), + &msg, + "SQLColAttributes(SQL_COLUMN_TABLE_NAME)")) { + rb_raise(Cerror, "%s", msg); + } + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + need_len = 6 * (uc_strlen(name) + 1); +#else + need_len = 2 * (strlen(name) + 1); +#endif + need += need_len; + if (max_len[0] < need_len) { + max_len[0] = need_len; + } + name[0] = 0; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, + (SQLUSMALLINT) (i + 1), + use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + sizeof (name), + &name_len, NULL), + &msg, use_scn ? + "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)")) { + rb_raise(Cerror, "%s", msg); + } + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } +#ifdef UNICODE + need_len = 6 * 2 * (uc_strlen(name) + 1); +#else + need_len = 2 * (strlen(name) + 1); +#endif + need += need_len; + if (max_len[1] < need_len) { + max_len[1] = need_len; + } + } + need += max_len[0] + max_len[1] + 32; + p = ALLOC_N(char, need); + if (p == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + na = (char **) p; + p += sizeof (char *) * 4 * q->ncols + sizeof (char *); + for (i = 0; i < q->ncols; i++) { + char *p0; + + name[0] = 0; + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, (SQLUSMALLINT) (i + 1), + SQL_COLUMN_TABLE_NAME, name, + sizeof (name), &name_len, NULL), + "SQLColAttributes(SQL_COLUMN_TABLE_NAME)"); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } + na[i + q->ncols] = p; +#ifdef UNICODE + p += mkutf(p, name, uc_strlen(name)); +#else + strcpy(p, name); +#endif + strcat(p, "."); + p += strlen(p); + p0 = p; + name[0] = 0; + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLColAttributes(q->hstmt, (SQLUSMALLINT) (i + 1), + use_scn ? SQL_COLUMN_NAME : + SQL_COLUMN_LABEL, name, + sizeof (name), &name_len, NULL), + use_scn ? "SQLColAttributes(SQL_COLUMN_NAME)" : + "SQLColAttributes(SQL_COLUMN_LABEL)"); + if (name_len >= (SQLSMALLINT) sizeof (name)) { + name_len = sizeof (name) - 1; + } + if (name_len > 0) { + name[name_len / sizeof (name[0])] = 0; + } + na[i] = p; +#ifdef UNICODE + p += mkutf(p, name, uc_strlen(name)) + 1; +#else + strcpy(p, name); + p += strlen(p) + 1; +#endif + na[i + 3 * q->ncols] = p; + strcpy(p, na[i + q->ncols]); + p += p0 - na[i + q->ncols]; + na[i + 2 * q->ncols] = upcase_if(p, 1); + p += strlen(p) + 1; + } + /* reserved space for later adjustments */ + na[4 * q->ncols] = p; + q->colnames = na; + if (q->colvals == NULL) { + q->colvals = ALLOC_N(VALUE, 4 * q->ncols); + if (q->colvals != NULL) { + VALUE cname; + VALUE colbuf[4]; + + for (i = 0; i < 4 * q->ncols; i++) { + q->colvals[i] = Qnil; + } + for (i = 0; i < 4; i++) { + colbuf[i] = rb_iv_get(q->self, colnamebuf[i]); + if (colbuf[i] == Qnil) { + res = rb_hash_new(); + rb_iv_set(q->self, colnamebuf[i], res); + } + } + for (i = 0; i < 4 * q->ncols; i++) { + res = colbuf[i / q->ncols]; + cname = rb_tainted_str_new2(q->colnames[i]); +#ifdef USE_RB_ENC + rb_enc_associate(cname, rb_enc); +#endif + q->colvals[i] = cname; + if (rb_funcall(res, IDkeyp, 1, cname) == Qtrue) { + char *p; + + cname = rb_tainted_str_new2(q->colnames[i]); +#ifdef USE_RB_ENC + rb_enc_associate(cname, rb_enc); +#endif + p = q->colnames[4 * q->ncols]; + sprintf(p, "#%d", i); + cname = rb_str_cat2(cname, p); + q->colvals[i] = cname; + } + rb_obj_freeze(cname); + rb_hash_aset(res, cname, Qtrue); + } + } + } + } + /* FALL THRU */ + case DOFETCH_HASHN: + if (mode & DOFETCH_BANG) { + res = rb_iv_get(q->self, "@_h"); + if (res == Qnil) { + res = rb_hash_new(); + rb_iv_set(q->self, "@_h", res); + } + } else { + res = rb_hash_new(); + } + break; + default: + if (mode & DOFETCH_BANG) { + res = rb_iv_get(q->self, "@_a"); + if (res == Qnil) { + res = rb_ary_new2(q->ncols); + rb_iv_set(q->self, "@_a", res); + } else { + rb_ary_clear(res); + } + } else { + res = rb_ary_new2(q->ncols); + } + } + offc = q->upc ? (2 * q->ncols) : 0; + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASHK2: + case DOFETCH_HASH2: + offc += q->ncols; + break; + } + for (i = 0; i < q->ncols; i++) { + SQLLEN totlen; + SQLLEN curlen = q->coltypes[i].size; + SQLSMALLINT type = q->coltypes[i].type; + VALUE v, name; + char *valp, *freep = NULL; + + if (curlen == SQL_NO_TOTAL) { + SQLLEN chunksize = SEGSIZE; + + totlen = 0; +#ifdef UNICODE + valp = ALLOC_N(char, chunksize + sizeof (SQLWCHAR)); +#else + valp = ALLOC_N(char, chunksize + 1); +#endif + freep = valp; + while ((curlen == SQL_NO_TOTAL) || (curlen > chunksize)) { + SQLRETURN rc; + int ret; + + rc = SQLGetData(q->hstmt, (SQLUSMALLINT) (i + 1), + type, (SQLPOINTER) (valp + totlen), +#ifdef UNICODE + ((type == SQL_C_CHAR) || (type == SQL_C_WCHAR)) ? + (chunksize + (int) sizeof (SQLWCHAR)) : + chunksize, +#else + (type == SQL_C_CHAR) ? + (chunksize + 1) : chunksize, +#endif + &curlen); + if (rc == SQL_NO_DATA) { + if (curlen == SQL_NO_TOTAL) { + curlen = totlen; + } + break; + } + ret = succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + rc, &msg, "SQLGetData"); + if (!ret) { + xfree(valp); + rb_raise(Cerror, "%s", msg); + } + if (curlen == SQL_NULL_DATA) { + break; + } + if (curlen == SQL_NO_TOTAL) { + totlen += chunksize; + } else if (curlen > chunksize) { + totlen += chunksize; + chunksize = curlen - chunksize; + } else { + totlen += curlen; + break; + } +#ifdef UNICODE + REALLOC_N(valp, char, totlen + chunksize + sizeof (SQLWCHAR)); +#else + REALLOC_N(valp, char, totlen + chunksize + 1); +#endif + if (valp == NULL) { + if (freep != NULL) { + xfree(freep); + } + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } + freep = valp; + } + if (totlen > 0) { + curlen = totlen; + } + } else { + totlen = curlen; + valp = bufs[i]; + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLGetData(q->hstmt, (SQLUSMALLINT) (i + 1), type, + (SQLPOINTER) valp, totlen, &curlen), + &msg, "SQLGetData")) { + rb_raise(Cerror, "%s", msg); + } + } + if (curlen == SQL_NULL_DATA) { + v = Qnil; + } else { + switch (type) { + case SQL_C_LONG: + v = INT2NUM(*((SQLINTEGER *) valp)); + break; + case SQL_C_DOUBLE: + v = rb_float_new(*((double *) valp)); + break; +#ifdef SQL_C_SBIGINT + case SQL_C_SBIGINT: +#ifdef LL2NUM + v = LL2NUM(*((SQLBIGINT *) valp)); +#else + v = INT2NUM(*((SQLBIGINT *) valp)); +#endif + break; +#endif +#ifdef SQL_C_UBIGINT + case SQL_C_UBIGINT: +#ifdef LL2NUM + v = ULL2NUM(*((SQLBIGINT *) valp)); +#else + v = UINT2NUM(*((SQLBIGINT *) valp)); +#endif + break; +#endif + case SQL_C_DATE: + { + DATE_STRUCT *date; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + const char *p; + char buffer[128]; + VALUE d; + + date = (DATE_STRUCT *) valp; + p = (q->dbcp->gmtime == Qtrue) ? "+00:00" : ""; + sprintf(buffer, "%d-%d-%dT00:00:00%s", + date->year, date->month, date->day, p); + d = rb_str_new2(buffer); + v = rb_funcall(rb_cDate, IDparse, 1, d); + } else { + v = Data_Make_Struct(Cdate, DATE_STRUCT, 0, xfree, + date); + *date = *(DATE_STRUCT *) valp; + } + } + break; + case SQL_C_TIME: + { + TIME_STRUCT *time; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE now, frac; + + time = (TIME_STRUCT *) valp; + frac = rb_float_new(0.0); + now = FUNCALL_NOARGS(rb_cTime, IDnow); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? + IDutc : IDlocal, + 7, + FUNCALL_NOARGS(now, IDyear), + FUNCALL_NOARGS(now, IDmonth), + FUNCALL_NOARGS(now, IDday), + INT2NUM(time->hour), + INT2NUM(time->minute), + INT2NUM(time->second), + frac); + } else { + v = Data_Make_Struct(Ctime, TIME_STRUCT, 0, xfree, + time); + *time = *(TIME_STRUCT *) valp; + } + } + break; + case SQL_C_TIMESTAMP: + { + TIMESTAMP_STRUCT *ts; + + if (q->dbcp != NULL && q->dbcp->rbtime == Qtrue) { + VALUE frac; + + ts = (TIMESTAMP_STRUCT *) valp; + frac = rb_float_new((double) 1.0e-3 * ts->fraction); + v = rb_funcall(rb_cTime, + (q->dbcp->gmtime == Qtrue) ? + IDutc : IDlocal, + 7, + INT2NUM(ts->year), + INT2NUM(ts->month), + INT2NUM(ts->day), + INT2NUM(ts->hour), + INT2NUM(ts->minute), + INT2NUM(ts->second), + frac); + } else { + v = Data_Make_Struct(Ctimestamp, TIMESTAMP_STRUCT, + 0, xfree, ts); + *ts = *(TIMESTAMP_STRUCT *) valp; + } + } + break; +#ifdef UNICODE + case SQL_C_WCHAR: + v = uc_tainted_str_new((SQLWCHAR *) valp, + curlen / sizeof (SQLWCHAR)); + break; +#endif + default: + v = rb_tainted_str_new(valp, curlen); + break; + } + } + if (freep != NULL) { + xfree(freep); + } + switch (mode & DOFETCH_MODES) { + case DOFETCH_HASH: + case DOFETCH_HASH2: + valp = q->colnames[i + offc]; + name = (q->colvals == NULL) ? Qnil : q->colvals[i + offc]; + if (name == Qnil) { + name = rb_tainted_str_new2(valp); +#ifdef USE_RB_ENC + rb_enc_associate(name, rb_enc); +#endif + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char *p; + + name = rb_tainted_str_new2(valp); +#ifdef USE_RB_ENC + rb_enc_associate(name, rb_enc); +#endif + p = q->colnames[4 * q->ncols]; + sprintf(p, "#%d", i); + name = rb_str_cat2(name, p); + } + } + rb_hash_aset(res, name, v); + break; + case DOFETCH_HASHK: + case DOFETCH_HASHK2: + valp = q->colnames[i + offc]; +#ifdef USE_RB_ENC + name = ID2SYM(rb_intern3(valp, strlen(valp), rb_enc)); +#else + name = ID2SYM(rb_intern(valp)); +#endif + if (rb_funcall(res, IDkeyp, 1, name) == Qtrue) { + char *p; + + p = q->colnames[4 * q->ncols]; + sprintf(p, "%s#%d", valp, i); +#ifdef USE_RB_ENC + name = ID2SYM(rb_intern3(p, strlen(p), rb_enc)); +#else + name = ID2SYM(rb_intern(p)); +#endif + } + rb_hash_aset(res, name, v); + break; + case DOFETCH_HASHN: + name = INT2NUM(i); + rb_hash_aset(res, name, v); + break; + default: + rb_ary_push(res, v); + } + } + return res; +} + +static VALUE +stmt_fetch1(VALUE self, int bang) +{ + STMT *q; + SQLRETURN ret; + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (q->usef) { + goto usef; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_NEXT)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_NEXT, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_NEXT)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_NEXT, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + if ((err != NULL) && + ((strncmp(err, "IM001", 5) == 0) || + (strncmp(err, "HYC00", 5) == 0))) { +usef: + /* Fallback to SQLFetch() when others not implemented */ + msg = "SQLFetch"; + q->usef = 1; + ret = SQLFETCH(q->hstmt); + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch(VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each(self); + } + return stmt_fetch1(self, 0); +} + +static VALUE +stmt_fetch_bang(VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each(self); + } + return stmt_fetch1(self, 1); +} + +static VALUE +stmt_fetch_first1(VALUE self, int bang, int nopos) +{ + STMT *q; + SQLRETURN ret; + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (nopos) { + goto dofetch; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_FIRST)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_FIRST)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { +dofetch: + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_first(VALUE self) +{ + return stmt_fetch_first1(self, 0, 0); +} + +static VALUE +stmt_fetch_first_bang(VALUE self) +{ + return stmt_fetch_first1(self, 1, 0); +} + +static VALUE +stmt_fetch_scroll1(int argc, VALUE *argv, VALUE self, int bang) +{ + STMT *q; + VALUE dir, offs; + SQLRETURN ret; + int idir, ioffs = 1; + char msg[128], *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + rb_scan_args(argc, argv, "11", &dir, &offs); + idir = NUM2INT(dir); + if (offs != Qnil) { + ioffs = NUM2INT(offs); + } + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } +#if (ODBCVER < 0x0300) + sprintf(msg, "SQLExtendedFetch(%d)", idir); + ret = SQLEXTENDEDFETCH(q->hstmt, (SQLSMALLINT) idir, (SQLROWOFFSET) ioffs, + &nRows, rowStat); +#else + sprintf(msg, "SQLFetchScroll(%d)", idir); + ret = SQLFETCHSCROLL(q->hstmt, (SQLSMALLINT) idir, (SQLROWOFFSET) ioffs); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, DOFETCH_ARY | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_scroll(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_scroll1(argc, argv, self, 0); +} + +static VALUE +stmt_fetch_scroll_bang(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_scroll1(argc, argv, self, 1); +} + +static VALUE +stmt_fetch_many(VALUE self, VALUE arg) +{ + int i, max = 0, all = arg == Qnil; + VALUE res; + + if (!all) { + max = NUM2INT(arg); + } + res = rb_ary_new(); + for (i = 0; all || (i < max); i++) { + VALUE v = stmt_fetch1(self, 0); + + if (v == Qnil) { + break; + } + rb_ary_push(res, v); + } + return (i == 0) ? Qnil : res; +} + +static VALUE +stmt_fetch_all(VALUE self) +{ + return stmt_fetch_many(self, Qnil); +} + +static int +stmt_hash_mode(int argc, VALUE *argv, VALUE self) +{ + VALUE withtab = Qnil, usesym = Qnil; + int mode = DOFETCH_HASH; + + rb_scan_args(argc, argv, "02", &withtab, &usesym); + if ((withtab != Qtrue) && (withtab != Qfalse) && (withtab != Modbc) && + (rb_obj_is_kind_of(withtab, rb_cHash) == Qtrue)) { + VALUE v; + + v = rb_hash_aref(withtab, ID2SYM(IDkey)); + if (v == ID2SYM(IDSymbol)) { + mode = DOFETCH_HASHK; + } else if (v == ID2SYM(IDString)) { + mode = DOFETCH_HASH; + } else if (v == ID2SYM(IDFixnum)) { + mode = DOFETCH_HASHN; + } else { + rb_raise(Cerror, "Unsupported key mode"); + } + if (mode != DOFETCH_HASHN) { + v = rb_hash_aref(withtab, ID2SYM(IDtable_names)); + if (RTEST(v)) { + mode = (mode == DOFETCH_HASHK) + ? DOFETCH_HASHK2 : DOFETCH_HASH2; + } + } + return mode; + } + if (withtab == Modbc) { + return DOFETCH_HASHN; + } + mode = RTEST(withtab) ? DOFETCH_HASH2 : DOFETCH_HASH; + if (RTEST(usesym)) { + mode = (mode == DOFETCH_HASH2) ? DOFETCH_HASHK2 : DOFETCH_HASHK; + } + return mode; +} + +static VALUE +stmt_fetch_hash1(int argc, VALUE *argv, VALUE self, int bang) +{ + STMT *q; + SQLRETURN ret; + int mode = stmt_hash_mode(argc, argv, self); + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (q->usef) { + goto usef; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_NEXT)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_NEXT, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_NEXT)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_NEXT, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + if ((err != NULL) && + ((strncmp(err, "IM001", 5) == 0) || + (strncmp(err, "HYC00", 5) == 0))) { +usef: + /* Fallback to SQLFetch() when others not implemented */ + msg = "SQLFetch"; + q->usef = 1; + ret = SQLFETCH(q->hstmt); + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &err, msg)) { + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_hash(int argc, VALUE *argv, VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each_hash(argc, argv, self); + } + return stmt_fetch_hash1(argc, argv, self, 0); +} + +static VALUE +stmt_fetch_hash_bang(int argc, VALUE *argv, VALUE self) +{ + if (rb_block_given_p()) { + return stmt_each_hash(argc, argv, self); + } + return stmt_fetch_hash1(argc, argv, self, 1); +} + +static VALUE +stmt_fetch_first_hash1(int argc, VALUE *argv, VALUE self, int bang, int nopos) +{ + STMT *q; + SQLRETURN ret; + int mode = stmt_hash_mode(argc, argv, self); + const char *msg; + char *err; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); + if (q->ncols <= 0) { + return Qnil; + } + if (nopos) { + goto dofetch; + } +#if (ODBCVER < 0x0300) + msg = "SQLExtendedFetch(SQL_FETCH_FIRST)"; + ret = SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, rowStat); +#else + msg = "SQLFetchScroll(SQL_FETCH_FIRST)"; + ret = SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0); +#endif + if (ret == SQL_NO_DATA) { + (void) tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, msg); + return Qnil; + } + if (succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, &err, msg)) { +dofetch: + return do_fetch(q, mode | (bang ? DOFETCH_BANG : 0)); + } + rb_raise(Cerror, "%s", err); + return Qnil; +} + +static VALUE +stmt_fetch_first_hash(int argc, VALUE *argv, VALUE self) +{ + return stmt_fetch_first_hash1(argc, argv, self, 0, 0); +} + +static VALUE +stmt_each(VALUE self) +{ + VALUE row, res = Qnil; + STMT *q; +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + Data_Get_Struct(self, STMT, q); +#if (ODBCVER < 0x0300) + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, + rowStat), + "SQLExtendedFetch(SQL_FETCH_FIRST)")) +#else + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0), + "SQLFetchScroll(SQL_FETCH_FIRST)")) +#endif + { + case SQL_NO_DATA: + row = Qnil; + break; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + row = stmt_fetch_first1(self, 0, 1); + break; + default: + row = stmt_fetch1(self, 0); + } + if (rb_block_given_p()) { + while (row != Qnil) { + rb_yield(row); + row = stmt_fetch1(self, 0); + } + return self; + } + if (row != Qnil) { + res = rb_ary_new(); + while (row != Qnil) { + rb_ary_push(res, row); + row = stmt_fetch1(self, 0); + } + } + return res; +} + +static VALUE +stmt_each_hash(int argc, VALUE *argv, VALUE self) +{ + VALUE row, res = Qnil, withtab[2]; + STMT *q; + int mode = stmt_hash_mode(argc, argv, self); +#if (ODBCVER < 0x0300) + SQLROWSETSIZE nRows; + SQLUSMALLINT rowStat[1]; +#endif + + if (mode == DOFETCH_HASHN) { + withtab[0] = Modbc; + withtab[1] = Qfalse; + } else { + withtab[0] = ((mode == DOFETCH_HASH2) || (mode == DOFETCH_HASHK2)) + ? Qtrue : Qfalse; + withtab[1] = ((mode == DOFETCH_HASHK) || (mode == DOFETCH_HASHK2)) + ? Qtrue : Qfalse; + } + Data_Get_Struct(self, STMT, q); +#if (ODBCVER < 0x0300) + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLEXTENDEDFETCH(q->hstmt, SQL_FETCH_FIRST, 0, &nRows, + rowStat), + "SQLExtendedFetch(SQL_FETCH_FIRST)")) +#else + switch (callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFETCHSCROLL(q->hstmt, SQL_FETCH_FIRST, 0), + "SQLFetchScroll(SQL_FETCH_FIRST)")) +#endif + { + case SQL_NO_DATA: + row = Qnil; + break; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + row = stmt_fetch_first_hash1(2, withtab, self, 0, 1); + break; + default: + row = stmt_fetch_hash1(2, withtab, self, 0); + } + if (rb_block_given_p()) { + while (row != Qnil) { + rb_yield(row); + row = stmt_fetch_hash1(2, withtab, self, 0); + } + return self; + } + if (row != Qnil) { + res = rb_ary_new(); + while (row != Qnil) { + rb_ary_push(res, row); + row = stmt_fetch_hash1(2, withtab, self, 0); + } + } + return res; +} + +static VALUE +stmt_more_results(VALUE self) +{ + STMT *q; + + if (rb_block_given_p()) { + rb_raise(rb_eArgError, "block not allowed"); + } + Data_Get_Struct(self, STMT, q); + if (q->hstmt == SQL_NULL_HSTMT) { + return Qfalse; + } + switch (tracesql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLMoreResults(q->hstmt), "SQLMoreResults")) { + case SQL_NO_DATA: + return Qfalse; + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + free_stmt_sub(q, 0); + make_result(q->dbc, q->hstmt, self, 0); + break; + default: + rb_raise(Cerror, "%s", + get_err(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt)); + } + return Qtrue; +} + +static VALUE +stmt_prep_int(int argc, VALUE *argv, VALUE self, int mode) +{ + DBC *p = get_dbc(self); + STMT *q = NULL; + VALUE sql, dbc, stmt; + SQLHSTMT hstmt; + SQLRETURN ret; +#ifdef UNICODE + SQLWCHAR *ssql = NULL; +#else + SQLCHAR *ssql = NULL; +#endif + char *csql = NULL, *msg = NULL; + + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + Data_Get_Struct(self, STMT, q); + free_stmt_sub(q, 0); + if (q->hstmt == SQL_NULL_HSTMT) { + if (!succeeded(SQL_NULL_HENV, p->hdbc, q->hstmt, + SQLAllocStmt(p->hdbc, &q->hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + } else if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), + &msg, "SQLFreeStmt(SQL_CLOSE)")) { + rb_raise(Cerror, "%s", msg); + } + hstmt = q->hstmt; + stmt = self; + dbc = q->dbc; + } else { + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + stmt = Qnil; + dbc = self; + } + rb_scan_args(argc, argv, "1", &sql); + Check_Type(sql, T_STRING); +#ifdef UNICODE +#ifdef USE_RB_ENC + sql = rb_funcall(sql, IDencode, 1, rb_encv); +#endif + csql = STR2CSTR(sql); + ssql = uc_from_utf((unsigned char *) csql, -1); + if (ssql == NULL) { + rb_raise(Cerror, "%s", set_err("Out of memory", 0)); + } +#else + csql = STR2CSTR(sql); + ssql = (SQLCHAR *) csql; +#endif + if ((mode & MAKERES_EXECD)) { + ret = SQLEXECDIRECT(hstmt, ssql, SQL_NTS); + if (!succeeded_nodata(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, ret, + &msg, "SQLExecDirect('%s')", csql)) { + goto sqlerr; + } + if (ret == SQL_NO_DATA) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_CLOSE), "SQLFreeStmt(SQL_DROP)"); + if (q != NULL) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + hstmt = SQL_NULL_HSTMT; + } + } else { + ret = SQLPREPARE(hstmt, ssql, SQL_NTS); + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, ret, + &msg, "SQLPrepare('%s')", csql)) { +sqlerr: +#ifdef UNICODE + uc_free(ssql); +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, hstmt, + SQLFreeStmt(hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + if (q != NULL) { + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + } + rb_raise(Cerror, "%s", msg); + } else { + mode |= MAKERES_PREPARE; + } + } +#ifdef UNICODE + uc_free(ssql); +#endif + return make_result(dbc, hstmt, stmt, mode); +} + +static VALUE +stmt_prep(int argc, VALUE *argv, VALUE self) +{ + return stmt_prep_int(argc, argv, self, MAKERES_BLOCK); +} + +static int +bind_one_param(int pnum, VALUE arg, STMT *q, char **msgp, int *outpp) +{ + SQLPOINTER valp = (SQLPOINTER) &q->paraminfo[pnum].buffer; + SQLSMALLINT ctype, stype; + SQLINTEGER vlen, rlen; + SQLUINTEGER coldef; +#ifdef NO_RB_STR2CSTR + VALUE val; +#endif + long llen; + int retry = 1; +#ifdef UNICODE + SQLWCHAR *up; + + q->paraminfo[pnum].tofree = NULL; +#endif + switch (TYPE(arg)) { + case T_STRING: +#ifdef UNICODE + ctype = SQL_C_WCHAR; +#ifdef USE_RB_ENC + arg = rb_funcall(arg, IDencode, 1, rb_encv); +#endif +#ifndef NO_RB_STR2CSTR + up = (SQLWCHAR *) rb_str2cstr(arg, &llen); + if (llen != (long) strlen((char *) up)) { + ctype = SQL_C_BINARY; + valp = (SQLPOINTER) up; + rlen = llen; + vlen = rlen + 1; + break; + } +#else + val = rb_string_value(&arg); + up = (SQLWCHAR *) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (up == NULL) { + goto oom; + } + if (memchr((char *) up, 0, llen)) { + ctype = SQL_C_BINARY; + valp = (SQLPOINTER) up; + rlen = llen; + vlen = rlen + 1; + break; + } + up = (SQLWCHAR *) rb_string_value_cstr(&arg); +#endif + up = uc_from_utf((unsigned char *) up, llen); + if (up == NULL) { + goto oom; + } + *(SQLWCHAR **) valp = up; + rlen = uc_strlen(up) * sizeof (SQLWCHAR); + vlen = rlen + sizeof (SQLWCHAR); + q->paraminfo[pnum].tofree = up; +#else + ctype = SQL_C_CHAR; +#ifndef NO_RB_STR2CSTR + valp = (SQLPOINTER) rb_str2cstr(arg, &llen); + rlen = llen; + if (rlen != (SQLINTEGER) strlen((char *) valp)) { + ctype = SQL_C_BINARY; + } + vlen = rlen + 1; +#else + val = rb_string_value(&arg); + valp = (SQLPOINTER) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (valp == NULL) { + goto oom; + } + rlen = llen; + vlen = rlen + 1; + if (memchr((char *) valp, 0, llen)) { + ctype = SQL_C_BINARY; + break; + } + valp = (SQLPOINTER) rb_string_value_cstr(&arg); +#endif +#endif + break; + case T_FIXNUM: + ctype = SQL_C_LONG; + *(SQLINTEGER *) valp = FIX2INT(arg); + rlen = 1; + vlen = sizeof (SQLINTEGER); + break; + case T_FLOAT: + ctype = SQL_C_DOUBLE; + *(double *) valp = NUM2DBL(arg); + rlen = 1; + vlen = sizeof (double); + break; + case T_NIL: + ctype = SQL_C_CHAR; + valp = NULL; + rlen = SQL_NULL_DATA; + vlen = 0; + break; + case T_SYMBOL: + ctype = SQL_C_CHAR; + valp = NULL; + vlen = 0; + if (arg == ID2SYM(IDNULL)) { + rlen = SQL_NULL_DATA; + } else if (arg == ID2SYM(IDdefault)) { + rlen = SQL_DEFAULT_PARAM; + } + /* fall through */ + default: + if (rb_obj_is_kind_of(arg, Cdate) == Qtrue) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + Data_Get_Struct(arg, DATE_STRUCT, date); + valp = (SQLPOINTER) date; + rlen = 1; + vlen = sizeof (DATE_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, Ctime) == Qtrue) { + TIME_STRUCT *time; + + ctype = SQL_C_TIME; + Data_Get_Struct(arg, TIME_STRUCT, time); + valp = (SQLPOINTER) time; + rlen = 1; + vlen = sizeof (TIME_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + ctype = SQL_C_TIMESTAMP; + Data_Get_Struct(arg, TIMESTAMP_STRUCT, ts); + valp = (SQLPOINTER) ts; + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + break; + } + if (rb_obj_is_kind_of(arg, rb_cTime) == Qtrue) { + if (q->paraminfo[pnum].type == SQL_TIME) { + TIME_STRUCT *time; + + ctype = SQL_C_TIME; + time = (TIME_STRUCT *) valp; + memset(time, 0, sizeof (TIME_STRUCT)); + time->hour = FUNCALL_NOARGS(arg, IDhour); + time->minute = FUNCALL_NOARGS(arg, IDmin); + time->second = FUNCALL_NOARGS(arg, IDsec); + rlen = 1; + vlen = sizeof (TIME_STRUCT); + } else if (q->paraminfo[pnum].type == SQL_DATE) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + date = (DATE_STRUCT *) valp; + memset(date, 0, sizeof (DATE_STRUCT)); + date->year = FUNCALL_NOARGS(arg, IDyear); + date->month = FUNCALL_NOARGS(arg, IDmonth); + date->day = FUNCALL_NOARGS(arg, IDday); + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + } else { + TIMESTAMP_STRUCT *ts; + + ctype = SQL_C_TIMESTAMP; + ts = (TIMESTAMP_STRUCT *) valp; + memset(ts, 0, sizeof (TIMESTAMP_STRUCT)); + ts->year = FUNCALL_NOARGS(arg, IDyear); + ts->month = FUNCALL_NOARGS(arg, IDmonth); + ts->day = FUNCALL_NOARGS(arg, IDday); + ts->hour = FUNCALL_NOARGS(arg, IDhour); + ts->minute = FUNCALL_NOARGS(arg, IDmin); + ts->second = FUNCALL_NOARGS(arg, IDsec); +#ifdef TIME_USE_USEC + ts->fraction = FUNCALL_NOARGS(arg, IDusec) * 1000; +#else + ts->fraction = FUNCALL_NOARGS(arg, IDnsec); +#endif + rlen = 1; + vlen = sizeof (TIMESTAMP_STRUCT); + } + break; + } + if (rb_obj_is_kind_of(arg, rb_cDate) == Qtrue) { + DATE_STRUCT *date; + + ctype = SQL_C_DATE; + date = (DATE_STRUCT *) valp; + memset(date, 0, sizeof (DATE_STRUCT)); + date->year = FUNCALL_NOARGS(arg, IDyear); + date->month = FUNCALL_NOARGS(arg, IDmonth); + date->day = FUNCALL_NOARGS(arg, IDmday); + rlen = 1; + vlen = sizeof (DATE_STRUCT); + break; + } + ctype = SQL_C_CHAR; +#ifndef NO_RB_STR2CSTR + valp = (SQLPOINTER *) rb_str2cstr(rb_str_to_str(arg), &llen); + rlen = llen; + if (rlen != (SQLINTEGER) strlen((char *) valp)) { + ctype = SQL_C_BINARY; + } + vlen = rlen + 1; +#else + val = rb_string_value(&arg); + valp = (SQLPOINTER) RSTRING_PTR(val); + llen = RSTRING_LEN(val); + if (valp == NULL) { + goto oom; + } + rlen = llen; + vlen = rlen + 1; + if (memchr((char *) valp, 0, llen)) { + ctype = SQL_C_BINARY; + break; + } + valp = (SQLPOINTER) rb_string_value_cstr(&arg); +#endif + break; + } + stype = q->paraminfo[pnum].type; + coldef = q->paraminfo[pnum].coldef; + q->paraminfo[pnum].rlen = rlen; + q->paraminfo[pnum].ctype = ctype; + if (coldef == 0) { + switch (ctype) { + case SQL_C_LONG: + coldef = 10; + break; + case SQL_C_DOUBLE: + coldef = 15; + if (stype == SQL_VARCHAR) { + stype = SQL_DOUBLE; + } + break; + case SQL_C_DATE: + coldef = 10; + break; + case SQL_C_TIME: + coldef = 8; + break; + case SQL_C_TIMESTAMP: + coldef = 19; + break; + default: + /* + * Patch adopted from the Perl DBD::ODBC module ... + * per patch from Paul G. Weiss, who was experiencing re-preparing + * of queries when the size of the bound string's were increasing + * for example select * from tabtest where name = ? + * then executing with 'paul' and then 'thomas' would cause + * SQLServer to prepare the query twice, but if we ran 'thomas' + * then 'paul', it would not re-prepare the query. The key seems + * to be allocating enough space for the largest parameter. + * TBD: the default for this should be a tunable parameter. + */ + if ((stype == SQL_VARCHAR) && + (q->paraminfo[pnum].iotype != SQL_PARAM_INPUT_OUTPUT) && + (q->paraminfo[pnum].iotype != SQL_PARAM_OUTPUT)) { + if (q->paraminfo[pnum].coldef_max == 0) { + q->paraminfo[pnum].coldef_max = (vlen > 128) ? vlen : 128; + } else { + /* bump up max, if needed */ + if (vlen > (SQLINTEGER) q->paraminfo[pnum].coldef_max) { + q->paraminfo[pnum].coldef_max = vlen; + } + } + coldef = q->paraminfo[pnum].coldef_max; + } else { + coldef = vlen; + } + break; + } + } + if ((q->paraminfo[pnum].iotype == SQL_PARAM_INPUT_OUTPUT) || + (q->paraminfo[pnum].iotype == SQL_PARAM_OUTPUT)) { + if (valp == NULL) { + if (q->paraminfo[pnum].outsize > 0) { + if (q->paraminfo[pnum].outbuf != NULL) { + xfree(q->paraminfo[pnum].outbuf); + } + q->paraminfo[pnum].outbuf = xmalloc(q->paraminfo[pnum].outsize); + if (q->paraminfo[pnum].outbuf == NULL) { + goto oom; + } + ctype = q->paraminfo[pnum].ctype = q->paraminfo[pnum].outtype; + outpp[0]++; + valp = q->paraminfo[pnum].outbuf; + vlen = q->paraminfo[pnum].outsize; + } + } else { + if (q->paraminfo[pnum].outbuf != NULL) { + xfree(q->paraminfo[pnum].outbuf); + } + q->paraminfo[pnum].outbuf = xmalloc(vlen); + if (q->paraminfo[pnum].outbuf == NULL) { +oom: +#ifdef UNICODE + if (q->paraminfo[pnum].tofree != NULL) { + uc_free(q->paraminfo[pnum].tofree); + q->paraminfo[pnum].tofree = NULL; + } +#endif + *msgp = set_err("Out of memory", 0); + return -1; + } +#ifdef UNICODE + if (ctype == SQL_C_WCHAR) { + memcpy(q->paraminfo[pnum].outbuf, *(SQLWCHAR **) valp, vlen); + } else +#endif + memcpy(q->paraminfo[pnum].outbuf, valp, vlen); +#ifdef UNICODE + if (ctype == SQL_C_WCHAR) { + *(SQLWCHAR **) valp = (SQLWCHAR *) q->paraminfo[pnum].outbuf; + } else +#endif + valp = q->paraminfo[pnum].outbuf; + outpp[0]++; + } + } +retry: +#ifdef UNICODE + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLBindParameter(q->hstmt, (SQLUSMALLINT) (pnum + 1), + q->paraminfo[pnum].iotype, + ctype, stype, coldef, + q->paraminfo[pnum].scale, + (ctype == SQL_C_WCHAR) ? + *(SQLWCHAR **) valp : valp, + vlen, &q->paraminfo[pnum].rlen), + msgp, "SQLBindParameter(%d)", pnum + 1)) +#else + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLBindParameter(q->hstmt, (SQLUSMALLINT) (pnum + 1), + q->paraminfo[pnum].iotype, + ctype, stype, coldef, + q->paraminfo[pnum].scale, + valp, vlen, &q->paraminfo[pnum].rlen), + msgp, "SQLBindParameter(%d)", pnum + 1)) +#endif + { + if (retry) { + retry = 0; + if (stype == SQL_VARCHAR) { + /* maybe MS Jet memo field */ + stype = SQL_LONGVARCHAR; + goto retry; + } +#ifdef UNICODE + if (stype == SQL_WVARCHAR) { + stype = SQL_WLONGVARCHAR; + goto retry; + } +#endif + } + return -1; + } + return 0; +} + +static VALUE +stmt_exec_int(int argc, VALUE *argv, VALUE self, int mode) +{ + STMT *q; + int i, argnum, has_out_parms = 0; + char *msg = NULL; + SQLRETURN ret; + + Data_Get_Struct(self, STMT, q); + if (argc > q->nump - ((EXEC_PARMXOUT(mode) < 0) ? 0 : 1)) { + rb_raise(Cerror, "%s", set_err("Too much parameters", 0)); + } + if (q->hstmt == SQL_NULL_HSTMT) { + rb_raise(Cerror, "%s", set_err("Stale ODBC::Statement", 0)); + } + if (!succeeded(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_CLOSE), + &msg, "SQLFreeStmt(SQL_CLOSE)")) { + goto error; + } + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_RESET_PARAMS), + "SQLFreeStmt(SQL_RESET_PARMS)"); + for (i = argnum = 0; i < q->nump; i++) { + VALUE arg; + + if (i == EXEC_PARMXOUT(mode)) { + if (bind_one_param(i, Qnil, q, &msg, &has_out_parms) < 0) { + goto error; + } + continue; + } + arg = (argnum < argc) ? argv[argnum++] : Qnil; + if (bind_one_param(i, arg, q, &msg, &has_out_parms) < 0) { + goto error; + } + } + ret = SQLEXECUTE(q->hstmt); + if (!succeeded_nodata(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, ret, + &msg, "SQLExecute")) { +error: +#ifdef UNICODE + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].tofree != NULL) { + uc_free(q->paraminfo[i].tofree); + q->paraminfo[i].tofree = NULL; + } + } +#endif + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_DROP), "SQLFreeStmt(SQL_DROP)"); + q->hstmt = SQL_NULL_HSTMT; + unlink_stmt(q); + rb_raise(Cerror, "%s", msg); + } +#ifdef UNICODE + for (i = 0; i < q->nump; i++) { + if (q->paraminfo[i].tofree != NULL) { + uc_free(q->paraminfo[i].tofree); + q->paraminfo[i].tofree = NULL; + } + } +#endif + if (!has_out_parms) { + callsql(SQL_NULL_HENV, SQL_NULL_HDBC, q->hstmt, + SQLFreeStmt(q->hstmt, SQL_RESET_PARAMS), + "SQLFreeStmt(SQL_RESET_PARAMS)"); + } + if (ret == SQL_NO_DATA) { + return Qnil; + } + return make_result(q->dbc, q->hstmt, self, mode); +} + +static VALUE +stmt_exec(int argc, VALUE *argv, VALUE self) +{ + return stmt_exec_int(argc, argv, self, MAKERES_BLOCK); +} + +static VALUE +stmt_run(int argc, VALUE *argv, VALUE self) +{ + if (argc < 1) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + if (argc == 1) { + return stmt_prep_int(1, argv, self, + MAKERES_EXECD | MAKERES_BLOCK); + } + return stmt_exec(argc - 1, argv + 1, stmt_prep_int(1, argv, self, 0)); +} + +static VALUE +stmt_do(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt; + + if (argc < 1) { + rb_raise(rb_eArgError, "wrong # of arguments"); + } + if (argc == 1) { + stmt = stmt_prep_int(1, argv, self, + MAKERES_EXECD | MAKERES_BLOCK | MAKERES_NOCLOSE); + } else { + stmt = stmt_prep_int(1, argv, self, 0); + stmt_exec_int(argc - 1, argv + 1, stmt, + MAKERES_BLOCK | MAKERES_NOCLOSE); + } + return rb_ensure(stmt_nrows, stmt, stmt_drop, stmt); +} + +static VALUE +stmt_ignorecase(int argc, VALUE *argv, VALUE self) +{ + VALUE onoff = Qnil; + int *flag = NULL; + + rb_scan_args(argc, argv, "01", &onoff); + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + STMT *q; + + Data_Get_Struct(self, STMT, q); + flag = &q->upc; + } else if (rb_obj_is_kind_of(self, Cdbc) == Qtrue) { + DBC *p; + + Data_Get_Struct(self, DBC, p); + flag = &p->upc; + } else { + rb_raise(rb_eTypeError, "ODBC::Statement or ODBC::Database expected"); + return Qnil; + } + if (argc > 0) { + *flag = RTEST(onoff); + } + return *flag ? Qtrue : Qfalse; +} + +/* + *---------------------------------------------------------------------- + * + * Create statement without implicit SQL prepare or execute. + * + *---------------------------------------------------------------------- + */ +static VALUE +stmt_new(VALUE self) +{ + DBC *p; + SQLHSTMT hstmt; + char *msg = NULL; + + Data_Get_Struct(self, DBC, p); + if (!succeeded(SQL_NULL_HENV, p->hdbc, SQL_NULL_HSTMT, + SQLAllocStmt(p->hdbc, &hstmt), + &msg, "SQLAllocStmt")) { + rb_raise(Cerror, "%s", msg); + } + return wrap_stmt(self, p, hstmt, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * Procedures with statements. + * + *---------------------------------------------------------------------- + */ + +static VALUE +stmt_proc_init(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt = (argc > 0) ? argv[0] : Qnil; + + if (rb_obj_is_kind_of(stmt, Cstmt) == Qtrue) { + rb_iv_set(self, "@statement", stmt); + rb_iv_set(self, "@return_output_param", (argc > 1) ? argv[1] : Qnil); + return self; + } + rb_raise(rb_eTypeError, "need ODBC::Statement as argument"); + return Qnil; +} + +static VALUE +stmt_proc_call(int argc, VALUE *argv, VALUE self) +{ + VALUE stmt, val; + + stmt = rb_iv_get(self, "@statement"); + val = rb_iv_get(self, "@return_output_param"); + if (RTEST(val)) { + int parnum = NUM2INT(val); + + stmt_exec_int(argc, argv, stmt, EXEC_PARMXNULL(parnum)); + rb_call_super(1, &stmt); + return stmt_param_output_value(1, &val, stmt); + } + stmt_exec_int(argc, argv, stmt, 0); + return rb_call_super(1, &stmt); +} + +static VALUE +stmt_proc(int argc, VALUE *argv, VALUE self) +{ + VALUE sql, ptype, psize, pnum = Qnil, stmt, args[2]; + int parnum = 0; + + rb_scan_args(argc, argv, "13", &sql, &ptype, &psize, &pnum); + if (!rb_block_given_p()) { + rb_raise(rb_eArgError, "block required"); + } + stmt = stmt_prep_int(1, &sql, self, 0); + if (argc == 1) { + return rb_funcall(Cproc, IDnew, 1, stmt); + } + if ((argc < 4) || (pnum == Qnil)) { + pnum = INT2NUM(parnum); + } else { + parnum = NUM2INT(pnum); + } + args[0] = pnum; + args[1] = INT2NUM(SQL_PARAM_OUTPUT); + stmt_param_iotype(2, args, stmt); + args[1] = ptype; + stmt_param_output_type(2, args, stmt); + if (argc > 2) { + args[1] = psize; + } else { + args[1] = INT2NUM(256); + } + stmt_param_output_size(2, args, stmt); + return rb_funcall(Cproc, IDnew, 2, stmt, pnum); +} + +static VALUE +stmt_procwrap(int argc, VALUE *argv, VALUE self) +{ + VALUE arg0 = Qnil, arg1 = Qnil; + + rb_scan_args(argc, argv, "02", &arg0, &arg1); + if (rb_obj_is_kind_of(self, Cstmt) == Qtrue) { + if (arg1 != Qnil) { + rb_raise(rb_eArgError, "wrong # arguments"); + } + arg1 = arg0; + arg0 = self; + } else if (rb_obj_is_kind_of(arg0, Cstmt) != Qtrue) { + rb_raise(rb_eTypeError, "need ODBC::Statement as 1st argument"); + } + return rb_funcall(Cproc, IDnew, 2, arg0, arg1); +} + +/* + *---------------------------------------------------------------------- + * + * Module functions. + * + *---------------------------------------------------------------------- + */ + +static VALUE +mod_dbcdisc(VALUE dbc) +{ + return dbc_disconnect(0, NULL, dbc); +} + +static VALUE +mod_connect(int argc, VALUE *argv, VALUE self) +{ + VALUE dbc = dbc_new(argc, argv, self); + + if (rb_block_given_p()) { + return rb_ensure(rb_yield, dbc, mod_dbcdisc, dbc); + } + return dbc; +} + +static VALUE +mod_2time(int argc, VALUE *argv, VALUE self) +{ + VALUE a1, a2; + VALUE y, m, d, hh, mm, ss, us; + int once = 0; + + rb_scan_args(argc, argv, "11", &a1, &a2); +again: + if (rb_obj_is_kind_of(a1, Ctimestamp) == Qtrue) { + TIMESTAMP_STRUCT *ts; + + if (argc > 1) { + rb_raise(rb_eArgError, "wrong # arguments(2 for 1)"); + } + Data_Get_Struct(a1, TIMESTAMP_STRUCT, ts); + y = INT2NUM(ts->year); + m = INT2NUM(ts->month); + d = INT2NUM(ts->day); + hh = INT2NUM(ts->hour); + mm = INT2NUM(ts->minute); + ss = INT2NUM(ts->second); + us = INT2NUM(ts->fraction / 1000); + goto mktime; + } + if (rb_obj_is_kind_of(a1, Cdate) == Qtrue) { + DATE_STRUCT *date; + + if (a2 != Qnil) { + if (rb_obj_is_kind_of(a2, Ctime) == Qtrue) { + TIME_STRUCT *time; + + Data_Get_Struct(a2, TIME_STRUCT, time); + hh = INT2NUM(time->hour); + mm = INT2NUM(time->minute); + ss = INT2NUM(time->second); + } else { + rb_raise(rb_eTypeError, "expecting ODBC::Time"); + } + } else { + hh = INT2FIX(0); + mm = INT2FIX(0); + ss = INT2FIX(0); + } + Data_Get_Struct(a1, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + us = INT2FIX(0); + } + if (rb_obj_is_kind_of(a1, Ctime) == Qtrue) { + TIME_STRUCT *time; + + if (a2 != Qnil) { + if (rb_obj_is_kind_of(a2, Cdate) == Qtrue) { + DATE_STRUCT *date; + + Data_Get_Struct(a2, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + } else { + rb_raise(rb_eTypeError, "expecting ODBC::Date"); + } + } else { + VALUE now = FUNCALL_NOARGS(rb_cTime, IDnow); + + y = rb_funcall(rb_cTime, IDyear, 1, now); + m = rb_funcall(rb_cTime, IDmonth, 1, now); + d = rb_funcall(rb_cTime, IDday, 1, now); + } + Data_Get_Struct(a1, TIME_STRUCT, time); + hh = INT2NUM(time->hour); + mm = INT2NUM(time->minute); + ss = INT2NUM(time->second); + us = INT2FIX(0); +mktime: + return rb_funcall(rb_cTime, IDlocal, 7, y, m, d, hh, mm, ss, us); + } + if ((!once) && ((m = timestamp_load1(Ctimestamp, a1, -1)) != Qnil)) { + a1 = m; + once++; + goto again; + } + if ((!once) && ((m = date_load1(Cdate, a1, -1)) != Qnil)) { + a1 = m; + if ((argc > 1) && ((m = time_load1(Ctime, a2, -1)) != Qnil)) { + a2 = m; + } + once++; + goto again; + } + if ((!once) && ((m = time_load1(Ctime, a1, -1)) != Qnil)) { + a1 = m; + if ((argc > 1) && ((m = date_load1(Cdate, a2, -1)) != Qnil)) { + a2 = m; + } + once++; + goto again; + } + rb_raise(rb_eTypeError, + "expecting ODBC::TimeStamp or ODBC::Date/Time or String"); + return Qnil; +} + +static VALUE +mod_2date(VALUE self, VALUE arg) +{ + VALUE y, m, d; + int once = 0; + +again: + if (rb_obj_is_kind_of(arg, Cdate) == Qtrue) { + DATE_STRUCT *date; + + Data_Get_Struct(arg, DATE_STRUCT, date); + y = INT2NUM(date->year); + m = INT2NUM(date->month); + d = INT2NUM(date->day); + goto mkdate; + } + if (rb_obj_is_kind_of(arg, Ctimestamp) == Qtrue){ + TIMESTAMP_STRUCT *ts; + + Data_Get_Struct(arg, TIMESTAMP_STRUCT, ts); + y = INT2NUM(ts->year); + m = INT2NUM(ts->month); + d = INT2NUM(ts->day); +mkdate: + return rb_funcall(rb_cDate, IDnew, 3, y, m, d); + } + if ((!once) && + (((m = date_load1(Cdate, arg, -1)) != Qnil) || + ((m = timestamp_load1(Ctimestamp, arg, -1)) != Qnil))) { + arg = m; + once++; + goto again; + } + rb_raise(rb_eTypeError, "expecting ODBC::Date/Timestamp or String"); + return Qnil; +} + +static VALUE +mod_trace(int argc, VALUE *argv, VALUE self) +{ + VALUE v = Qnil; + + rb_scan_args(argc, argv, "01", &v); +#ifdef TRACING + if (argc > 0) { + tracing = NUM2INT(v); + } + return INT2NUM(tracing); +#else + return INT2NUM(0); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * Table of constants and intern'ed string mappings. + * + *---------------------------------------------------------------------- + */ + +#define O_CONST(x) { #x, x } +#define O_CONSTU(x) { #x, SQL_UNKNOWN_TYPE } +#define O_CONST2(x,y) { #x, y } +#define O_CONST_END { NULL, -1 } + +static struct { + const char *name; + int value; +} o_const[] = { + O_CONST(SQL_CURSOR_FORWARD_ONLY), + O_CONST(SQL_CURSOR_KEYSET_DRIVEN), + O_CONST(SQL_CURSOR_DYNAMIC), + O_CONST(SQL_CURSOR_STATIC), + O_CONST(SQL_CONCUR_READ_ONLY), + O_CONST(SQL_CONCUR_LOCK), + O_CONST(SQL_CONCUR_ROWVER), + O_CONST(SQL_CONCUR_VALUES), + O_CONST(SQL_FETCH_NEXT), + O_CONST(SQL_FETCH_FIRST), + O_CONST(SQL_FETCH_LAST), + O_CONST(SQL_FETCH_PRIOR), + O_CONST(SQL_FETCH_ABSOLUTE), + O_CONST(SQL_FETCH_RELATIVE), + O_CONST(SQL_UNKNOWN_TYPE), + O_CONST(SQL_CHAR), + O_CONST(SQL_NUMERIC), + O_CONST(SQL_DECIMAL), + O_CONST(SQL_INTEGER), + O_CONST(SQL_SMALLINT), + O_CONST(SQL_FLOAT), + O_CONST(SQL_REAL), + O_CONST(SQL_DOUBLE), + O_CONST(SQL_VARCHAR), +#ifdef SQL_DATETIME + O_CONST(SQL_DATETIME), +#else + O_CONSTU(SQL_DATETIME), +#endif +#ifdef SQL_DATE + O_CONST(SQL_DATE), +#else + O_CONSTU(SQL_DATE), +#endif +#ifdef SQL_TYPE_DATE + O_CONST(SQL_TYPE_DATE), +#else + O_CONSTU(SQL_TYPE_DATE), +#endif +#ifdef SQL_TIME + O_CONST(SQL_TIME), +#else + O_CONSTU(SQL_TIME), +#endif +#ifdef SQL_TYPE_TIME + O_CONST(SQL_TYPE_TIME), +#else + O_CONSTU(SQL_TYPE_TIME), +#endif +#ifdef SQL_TIMESTAMP + O_CONST(SQL_TIMESTAMP), +#else + O_CONSTU(SQL_TIMESTAMP), +#endif +#ifdef SQL_TYPE_TIMESTAMP + O_CONST(SQL_TYPE_TIMESTAMP), +#else + O_CONSTU(SQL_TYPE_TIMESTAMP), +#endif +#ifdef SQL_LONGVARCHAR + O_CONST(SQL_LONGVARCHAR), +#else + O_CONSTU(SQL_LONGVARCHAR), +#endif +#ifdef SQL_BINARY + O_CONST(SQL_BINARY), +#else + O_CONSTU(SQL_BINARY), +#endif +#ifdef SQL_VARBINARY + O_CONST(SQL_VARBINARY), +#else + O_CONSTU(SQL_VARBINARY), +#endif +#ifdef SQL_LONGVARBINARY + O_CONST(SQL_LONGVARBINARY), +#else + O_CONSTU(SQL_LONGVARBINARY), +#endif +#ifdef SQL_BIGINT + O_CONST(SQL_BIGINT), +#else + O_CONSTU(SQL_BIGINT), +#endif +#ifdef SQL_TINYINT + O_CONST(SQL_TINYINT), +#else + O_CONSTU(SQL_TINYINT), +#endif +#ifdef SQL_BIT + O_CONST(SQL_BIT), +#else + O_CONSTU(SQL_BIT), +#endif +#ifdef SQL_GUID + O_CONST(SQL_GUID), +#else + O_CONSTU(SQL_GUID), +#endif +#ifdef SQL_WCHAR + O_CONST(SQL_WCHAR), +#else + O_CONSTU(SQL_WCHAR), +#endif +#ifdef SQL_WVARCHAR + O_CONST(SQL_WVARCHAR), +#else + O_CONSTU(SQL_WVARCHAR), +#endif +#ifdef SQL_WLONGVARCHAR + O_CONST(SQL_WLONGVARCHAR), +#else + O_CONSTU(SQL_WLONGVARCHAR), +#endif +#ifdef SQL_ATTR_ODBC_VERSION + O_CONST(SQL_OV_ODBC2), + O_CONST(SQL_OV_ODBC3), +#else + O_CONST2(SQL_OV_ODBC2, 2), + O_CONST2(SQL_OV_ODBC3, 3), +#endif +#ifdef SQL_ATTR_CONNECTION_POOLING +#ifdef SQL_CP_OFF + O_CONST(SQL_CP_OFF), +#else + O_CONST2(SQL_CP_OFF, 0), +#endif +#ifdef SQL_CP_ONE_PER_DRIVER + O_CONST(SQL_CP_ONE_PER_DRIVER), +#else + O_CONST2(SQL_CP_ONE_PER_DRIVER, 1), +#endif +#ifdef SQL_CP_ONE_PER_HENV + O_CONST(SQL_CP_ONE_PER_HENV), +#else + O_CONST2(SQL_CP_ONE_PER_HENV, 2), +#endif +#ifdef SQL_CP_DEFAULT + O_CONST(SQL_CP_DEFAULT), +#else + O_CONST2(SQL_CP_DEFAULT, 0), +#endif +#else + O_CONST2(SQL_CP_OFF, 0), + O_CONST2(SQL_CP_ONE_PER_DRIVER, 0), + O_CONST2(SQL_CP_ONE_PER_HENV, 0), + O_CONST2(SQL_CP_DEFAULT, 0), +#endif +#ifdef SQL_ATTR_CP_MATCH + O_CONST(SQL_CP_STRICT_MATCH), + O_CONST(SQL_CP_RELAXED_MATCH), + O_CONST(SQL_CP_MATCH_DEFAULT), +#else + O_CONST2(SQL_CP_STRICT_MATCH, 0), + O_CONST2(SQL_CP_RELAXED_MATCH, 0), + O_CONST2(SQL_CP_MATCH_DEFAULT, 0), +#endif +#ifdef SQL_SCOPE_CURROW + O_CONST(SQL_SCOPE_CURROW), +#else + O_CONST2(SQL_SCOPE_CURROW, 0), +#endif +#ifdef SQL_SCOPE_TRANSACTION + O_CONST(SQL_SCOPE_TRANSACTION), +#else + O_CONST2(SQL_SCOPE_TRANSACTION, 0), +#endif +#ifdef SQL_SCOPE_SESSION + O_CONST(SQL_SCOPE_SESSION), +#else + O_CONST2(SQL_SCOPE_SESSION, 0), +#endif +#ifdef SQL_BEST_ROWID + O_CONST(SQL_BEST_ROWID), +#else + O_CONST2(SQL_BEST_ROWID, 0), +#endif +#ifdef SQL_ROWVER + O_CONST(SQL_ROWVER), +#else + O_CONST2(SQL_ROWVER, 0), +#endif + O_CONST(SQL_PARAM_TYPE_UNKNOWN), + O_CONST(SQL_PARAM_INPUT), + O_CONST(SQL_PARAM_OUTPUT), + O_CONST(SQL_PARAM_INPUT_OUTPUT), + O_CONST(SQL_DEFAULT_PARAM), + O_CONST(SQL_RETURN_VALUE), + O_CONST(SQL_RESULT_COL), + O_CONST(SQL_PT_UNKNOWN), + O_CONST(SQL_PT_PROCEDURE), + O_CONST(SQL_PT_FUNCTION), + + /* end of table */ + O_CONST_END +}; + +static struct { + ID *idp; + const char *str; +} ids[] = { + { &IDstart, "start" }, + { &IDatatinfo, "@@info" }, + { &IDataterror, "@@error" }, + { &IDkeys, "keys" }, + { &IDatattrs, "@attrs" }, + { &IDday, "day" }, + { &IDmonth, "month" }, + { &IDyear, "year" }, + { &IDmday, "mday" }, + { &IDnsec, "nsec" }, + { &IDusec, "usec" }, + { &IDsec, "sec" }, + { &IDmin, "min" }, + { &IDhour, "hour" }, + { &IDusec, "usec" }, + { &IDkeyp, "key?" }, + { &IDkey, "key" }, + { &IDSymbol, "Symbol" }, + { &IDString, "String" }, + { &IDFixnum, "Fixnum" }, + { &IDtable_names, "table_names" }, + { &IDnew, "new" }, + { &IDnow, "now" }, + { &IDlocal, "local" }, + { &IDname, "name" }, + { &IDtable, "table" }, + { &IDtype, "type" }, + { &IDlength, "length" }, + { &IDnullable, "nullable" }, + { &IDscale, "scale" }, + { &IDprecision, "precision" }, + { &IDsearchable, "searchable" }, + { &IDunsigned, "unsigned" }, + { &IDiotype, "iotype" }, + { &IDoutput_size, "output_size" }, + { &IDoutput_type, "output_type" }, + { &IDdescr, "descr" }, + { &IDstatement, "statement" }, + { &IDreturn_output_param, "return_output_param" }, + { &IDattrs, "attrs" }, + { &IDNULL, "NULL" }, + { &IDdefault, "default" }, +#ifdef USE_RB_ENC + { &IDencode, "encode" }, +#endif + { &IDparse, "parse" }, + { &IDutc, "utc" }, + { &IDlocal, "local" }, + { &IDto_s, "to_s" } +}; + +/* + *---------------------------------------------------------------------- + * + * Module initializer. + * + *---------------------------------------------------------------------- + */ + +void +#ifdef UNICODE +Init_odbc_utf8() +#else +Init_odbc() +#endif +{ + int i; + const char *modname = "ODBC"; + ID modid = rb_intern(modname); + VALUE v = Qnil; + + rb_require("date"); + rb_cDate = rb_eval_string("Date"); + + if (rb_const_defined(rb_cObject, modid)) { + v = rb_const_get(rb_cObject, modid); + if (TYPE(v) != T_MODULE) { + rb_raise(rb_eTypeError, "%s already defined", modname); + } + } + if (v != Qnil) { +#ifdef UNICODE + modname = "ODBC_UTF8"; +#else + modname = "ODBC_NONE"; +#endif + } + + for (i = 0; i < (int) (sizeof (ids) / sizeof (ids[0])); i++) { + *(ids[i].idp) = rb_intern(ids[i].str); + } + + Modbc = rb_define_module(modname); + Cobj = rb_define_class_under(Modbc, "Object", rb_cObject); + rb_define_class_variable(Cobj, "@@error", Qnil); + rb_define_class_variable(Cobj, "@@info", Qnil); + + Cenv = rb_define_class_under(Modbc, "Environment", Cobj); + Cdbc = rb_define_class_under(Modbc, "Database", Cenv); + Cstmt = rb_define_class_under(Modbc, "Statement", Cdbc); + rb_include_module(Cstmt, rb_mEnumerable); + + Ccolumn = rb_define_class_under(Modbc, "Column", Cobj); + rb_attr(Ccolumn, IDname, 1, 0, Qfalse); + rb_attr(Ccolumn, IDtable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDtype, 1, 0, Qfalse); + rb_attr(Ccolumn, IDlength, 1, 0, Qfalse); + rb_attr(Ccolumn, IDnullable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDscale, 1, 0, Qfalse); + rb_attr(Ccolumn, IDprecision, 1, 0, Qfalse); + rb_attr(Ccolumn, IDsearchable, 1, 0, Qfalse); + rb_attr(Ccolumn, IDunsigned, 1, 0, Qfalse); + + Cparam = rb_define_class_under(Modbc, "Parameter", Cobj); + rb_attr(Cparam, IDtype, 1, 0, Qfalse); + rb_attr(Cparam, IDprecision, 1, 0, Qfalse); + rb_attr(Cparam, IDscale, 1, 0, Qfalse); + rb_attr(Cparam, IDnullable, 1, 0, Qfalse); + rb_attr(Cparam, IDiotype, 1, 0, Qfalse); + rb_attr(Cparam, IDoutput_size, 1, 0, Qfalse); + rb_attr(Cparam, IDoutput_type, 1, 0, Qfalse); + + Cdsn = rb_define_class_under(Modbc, "DSN", Cobj); + rb_attr(Cdsn, IDname, 1, 1, Qfalse); + rb_attr(Cdsn, IDdescr, 1, 1, Qfalse); + + Cdrv = rb_define_class_under(Modbc, "Driver", Cobj); + rb_attr(Cdrv, IDname, 1, 1, Qfalse); + rb_attr(Cdrv, IDattrs, 1, 1, Qfalse); + + Cerror = rb_define_class_under(Modbc, "Error", rb_eStandardError); + + Cproc = rb_define_class("ODBCProc", rb_cProc); + + Cdate = rb_define_class_under(Modbc, "Date", Cobj); + rb_include_module(Cdate, rb_mComparable); + Ctime = rb_define_class_under(Modbc, "Time", Cobj); + rb_include_module(Ctime, rb_mComparable); + Ctimestamp = rb_define_class_under(Modbc, "TimeStamp", Cobj); + rb_include_module(Ctimestamp, rb_mComparable); + + /* module functions */ + rb_define_module_function(Modbc, "trace", mod_trace, -1); + rb_define_module_function(Modbc, "trace=", mod_trace, -1); + rb_define_module_function(Modbc, "connect", mod_connect, -1); + rb_define_module_function(Modbc, "datasources", dbc_dsns, 0); + rb_define_module_function(Modbc, "drivers", dbc_drivers, 0); + rb_define_module_function(Modbc, "error", dbc_error, 0); + rb_define_module_function(Modbc, "info", dbc_warn, 0); + rb_define_module_function(Modbc, "clear_error", dbc_clrerror, 0); + rb_define_module_function(Modbc, "newenv", env_new, 0); + rb_define_module_function(Modbc, "to_time", mod_2time, -1); + rb_define_module_function(Modbc, "to_date", mod_2date, 1); + rb_define_module_function(Modbc, "connection_pooling", env_cpooling, -1); + rb_define_module_function(Modbc, "connection_pooling=", env_cpooling, -1); + rb_define_module_function(Modbc, "raise", dbc_raise, 1); + + /* singleton methods and constructors */ + rb_define_singleton_method(Cobj, "error", dbc_error, 0); + rb_define_singleton_method(Cobj, "info", dbc_warn, 0); + rb_define_singleton_method(Cobj, "clear_error", dbc_clrerror, 0); + rb_define_singleton_method(Cobj, "raise", dbc_raise, 1); + rb_define_alloc_func(Cenv, env_new); + rb_define_singleton_method(Cenv, "connect", dbc_new, -1); +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Cdbc, dbc_alloc); +#else + rb_define_alloc_func(Cdbc, dbc_new); + rb_define_alloc_func(Cdsn, dsn_new); + rb_define_alloc_func(Cdrv, drv_new); +#endif + rb_define_method(Cdsn, "initialize", dsn_init, 0); + rb_define_method(Cdrv, "initialize", drv_init, 0); + rb_define_method(Cdbc, "newstmt", stmt_new, 0); + + /* common (Cobj) methods */ + rb_define_method(Cobj, "error", dbc_error, 0); + rb_define_method(Cobj, "info", dbc_warn, 0); + rb_define_method(Cobj, "clear_error", dbc_clrerror, 0); + rb_define_method(Cobj, "raise", dbc_raise, 1); + + /* common (Cenv) methods */ + rb_define_method(Cenv, "connect", dbc_new, -1); + rb_define_method(Cenv, "environment", env_of, 0); + rb_define_method(Cenv, "transaction", dbc_transaction, 0); + rb_define_method(Cenv, "commit", dbc_commit, 0); + rb_define_method(Cenv, "rollback", dbc_rollback, 0); + rb_define_method(Cenv, "connection_pooling", env_cpooling, -1); + rb_define_method(Cenv, "connection_pooling=", env_cpooling, -1); + rb_define_method(Cenv, "cp_match", env_cpmatch, -1); + rb_define_method(Cenv, "cp_match=", env_cpmatch, -1); + rb_define_method(Cenv, "odbc_version", env_odbcver, -1); + rb_define_method(Cenv, "odbc_version=", env_odbcver, -1); + + /* management things (odbcinst.h) */ + rb_define_module_function(Modbc, "add_dsn", dbc_adddsn, -1); + rb_define_module_function(Modbc, "config_dsn", dbc_confdsn, -1); + rb_define_module_function(Modbc, "del_dsn", dbc_deldsn, -1); + rb_define_module_function(Modbc, "write_file_dsn", dbc_wfdsn, -1); + rb_define_module_function(Modbc, "read_file_dsn", dbc_rfdsn, -1); + + /* connection (database) methods */ + rb_define_method(Cdbc, "initialize", dbc_connect, -1); + rb_define_method(Cdbc, "connect", dbc_connect, -1); + rb_define_method(Cdbc, "connected?", dbc_connected, 0); + rb_define_method(Cdbc, "drvconnect", dbc_drvconnect, 1); + rb_define_method(Cdbc, "drop_all", dbc_dropall, 0); + rb_define_method(Cdbc, "disconnect", dbc_disconnect, -1); + rb_define_method(Cdbc, "tables", dbc_tables, -1); + rb_define_method(Cdbc, "columns", dbc_columns, -1); + rb_define_method(Cdbc, "primary_keys", dbc_primkeys, -1); + rb_define_method(Cdbc, "indexes", dbc_indexes, -1); + rb_define_method(Cdbc, "types", dbc_types, -1); + rb_define_method(Cdbc, "foreign_keys", dbc_forkeys, -1); + rb_define_method(Cdbc, "table_privileges", dbc_tpriv, -1); + rb_define_method(Cdbc, "procedures", dbc_procs, -1); + rb_define_method(Cdbc, "procedure_columns", dbc_proccols, -1); + rb_define_method(Cdbc, "special_columns", dbc_speccols, -1); + rb_define_method(Cdbc, "get_info", dbc_getinfo, -1); + rb_define_method(Cdbc, "prepare", stmt_prep, -1); + rb_define_method(Cdbc, "run", stmt_run, -1); + rb_define_method(Cdbc, "do", stmt_do, -1); + rb_define_method(Cdbc, "proc", stmt_proc, -1); + rb_define_method(Cdbc, "use_time", dbc_timefmt, -1); + rb_define_method(Cdbc, "use_time=", dbc_timefmt, -1); + rb_define_method(Cdbc, "use_utc", dbc_timeutc, -1); + rb_define_method(Cdbc, "use_utc=", dbc_timeutc, -1); + rb_define_method(Cdbc, "use_sql_column_name", dbc_use_scn, -1); + rb_define_method(Cdbc, "use_sql_column_name=", dbc_use_scn, -1); + + /* connection options */ + rb_define_method(Cdbc, "get_option", dbc_getsetoption, -1); + rb_define_method(Cdbc, "set_option", dbc_getsetoption, -1); + rb_define_method(Cdbc, "autocommit", dbc_autocommit, -1); + rb_define_method(Cdbc, "autocommit=", dbc_autocommit, -1); + rb_define_method(Cdbc, "concurrency", dbc_concurrency, -1); + rb_define_method(Cdbc, "concurrency=", dbc_concurrency, -1); + rb_define_method(Cdbc, "maxrows", dbc_maxrows, -1); + rb_define_method(Cdbc, "maxrows=", dbc_maxrows, -1); + rb_define_method(Cdbc, "timeout", dbc_timeout, -1); + rb_define_method(Cdbc, "timeout=", dbc_timeout, -1); + rb_define_method(Cdbc, "login_timeout", dbc_login_timeout, -1); + rb_define_method(Cdbc, "login_timeout=", dbc_login_timeout, -1); + rb_define_method(Cdbc, "maxlength", dbc_maxlength, -1); + rb_define_method(Cdbc, "maxlength=", dbc_maxlength, -1); + rb_define_method(Cdbc, "rowsetsize", dbc_rowsetsize, -1); + rb_define_method(Cdbc, "cursortype", dbc_cursortype, -1); + rb_define_method(Cdbc, "cursortype=", dbc_cursortype, -1); + rb_define_method(Cdbc, "noscan", dbc_noscan, -1); + rb_define_method(Cdbc, "noscan=", dbc_noscan, -1); + rb_define_method(Cdbc, "ignorecase", stmt_ignorecase, -1); + rb_define_method(Cdbc, "ignorecase=", stmt_ignorecase, -1); + + /* statement methods */ + rb_define_method(Cstmt, "drop", stmt_drop, 0); + rb_define_method(Cstmt, "close", stmt_close, 0); + rb_define_method(Cstmt, "cancel", stmt_cancel, 0); + rb_define_method(Cstmt, "column", stmt_column, -1); + rb_define_method(Cstmt, "columns", stmt_columns, -1); + rb_define_method(Cstmt, "parameter", stmt_param, -1); + rb_define_method(Cstmt, "parameters", stmt_params, 0); + rb_define_method(Cstmt, "param_type", stmt_param_type, -1); + rb_define_method(Cstmt, "param_iotype", stmt_param_iotype, -1); + rb_define_method(Cstmt, "param_output_size", stmt_param_output_size, -1); + rb_define_method(Cstmt, "param_output_type", stmt_param_output_type, -1); + rb_define_method(Cstmt, "param_output_value", stmt_param_output_value, -1); + rb_define_method(Cstmt, "ncols", stmt_ncols, 0); + rb_define_method(Cstmt, "nrows", stmt_nrows, 0); + rb_define_method(Cstmt, "nparams", stmt_nparams, 0); + rb_define_method(Cstmt, "cursorname", stmt_cursorname, -1); + rb_define_method(Cstmt, "cursorname=", stmt_cursorname, -1); + rb_define_method(Cstmt, "fetch", stmt_fetch, 0); + rb_define_method(Cstmt, "fetch!", stmt_fetch_bang, 0); + rb_define_method(Cstmt, "fetch_first", stmt_fetch_first, 0); + rb_define_method(Cstmt, "fetch_first!", stmt_fetch_first_bang, 0); + rb_define_method(Cstmt, "fetch_scroll", stmt_fetch_scroll, -1); + rb_define_method(Cstmt, "fetch_scroll!", stmt_fetch_scroll_bang, -1); + rb_define_method(Cstmt, "fetch_hash", stmt_fetch_hash, -1); + rb_define_method(Cstmt, "fetch_hash!", stmt_fetch_hash_bang, -1); + rb_define_method(Cstmt, "fetch_first_hash", stmt_fetch_first_hash, -1); + rb_define_method(Cstmt, "fetch_many", stmt_fetch_many, 1); + rb_define_method(Cstmt, "fetch_all", stmt_fetch_all, 0); + rb_define_method(Cstmt, "each", stmt_each, 0); + rb_define_method(Cstmt, "each_hash", stmt_each_hash, -1); + rb_define_method(Cstmt, "execute", stmt_exec, -1); + rb_define_method(Cstmt, "make_proc", stmt_procwrap, -1); + rb_define_method(Cstmt, "more_results", stmt_more_results, 0); + rb_define_method(Cstmt, "prepare", stmt_prep, -1); + rb_define_method(Cstmt, "run", stmt_run, -1); + rb_define_singleton_method(Cstmt, "make_proc", stmt_procwrap, -1); + + /* statement options */ + rb_define_method(Cstmt, "get_option", stmt_getsetoption, -1); + rb_define_method(Cstmt, "set_option", stmt_getsetoption, -1); + rb_define_method(Cstmt, "concurrency", stmt_concurrency, -1); + rb_define_method(Cstmt, "concurrency=", stmt_concurrency, -1); + rb_define_method(Cstmt, "maxrows", stmt_maxrows, -1); + rb_define_method(Cstmt, "maxrows=", stmt_maxrows, -1); + rb_define_method(Cstmt, "timeout", stmt_timeout, -1); + rb_define_method(Cstmt, "timeout=", stmt_timeout, -1); + rb_define_method(Cstmt, "maxlength", stmt_maxlength, -1); + rb_define_method(Cstmt, "maxlength=", stmt_maxlength, -1); + rb_define_method(Cstmt, "cursortype", stmt_cursortype, -1); + rb_define_method(Cstmt, "cursortype=", stmt_cursortype, -1); + rb_define_method(Cstmt, "noscan", stmt_noscan, -1); + rb_define_method(Cstmt, "noscan=", stmt_noscan, -1); + rb_define_method(Cstmt, "rowsetsize", stmt_rowsetsize, -1); + + /* data type methods */ +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Cdate, date_alloc); +#else + rb_define_singleton_method(Cdate, "new", date_new, -1); +#endif + rb_define_singleton_method(Cdate, "_load", date_load, 1); + rb_define_method(Cdate, "initialize", date_init, -1); + rb_define_method(Cdate, "clone", date_clone, 0); + rb_define_method(Cdate, "to_s", date_to_s, 0); + rb_define_method(Cdate, "_dump", date_dump, 1); + rb_define_method(Cdate, "inspect", date_inspect, 0); + rb_define_method(Cdate, "year", date_year, -1); + rb_define_method(Cdate, "month", date_month, -1); + rb_define_method(Cdate, "day", date_day, -1); + rb_define_method(Cdate, "year=", date_year, -1); + rb_define_method(Cdate, "month=", date_month, -1); + rb_define_method(Cdate, "day=", date_day, -1); + rb_define_method(Cdate, "<=>", date_cmp, 1); + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Ctime, time_alloc); +#else + rb_define_singleton_method(Ctime, "new", time_new, -1); +#endif + rb_define_singleton_method(Ctime, "_load", time_load, 1); + rb_define_method(Ctime, "initialize", time_init, -1); + rb_define_method(Ctime, "clone", time_clone, 0); + rb_define_method(Ctime, "to_s", time_to_s, 0); + rb_define_method(Ctime, "_dump", time_dump, 1); + rb_define_method(Ctime, "inspect", time_inspect, 0); + rb_define_method(Ctime, "hour", time_hour, -1); + rb_define_method(Ctime, "minute", time_min, -1); + rb_define_method(Ctime, "second", time_sec, -1); + rb_define_method(Ctime, "hour=", time_hour, -1); + rb_define_method(Ctime, "minute=", time_min, -1); + rb_define_method(Ctime, "second=", time_sec, -1); + rb_define_method(Ctime, "<=>", time_cmp, 1); + +#ifdef HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(Ctimestamp, timestamp_alloc); +#else + rb_define_singleton_method(Ctimestamp, "new", timestamp_new, -1); +#endif + rb_define_singleton_method(Ctimestamp, "_load", timestamp_load, 1); + rb_define_method(Ctimestamp, "initialize", timestamp_init, -1); + rb_define_method(Ctimestamp, "clone", timestamp_clone, 0); + rb_define_method(Ctimestamp, "to_s", timestamp_to_s, 0); + rb_define_method(Ctimestamp, "_dump", timestamp_dump, 1); + rb_define_method(Ctimestamp, "inspect", timestamp_inspect, 0); + rb_define_method(Ctimestamp, "year", timestamp_year, -1); + rb_define_method(Ctimestamp, "month", timestamp_month, -1); + rb_define_method(Ctimestamp, "day", timestamp_day, -1); + rb_define_method(Ctimestamp, "hour", timestamp_hour, -1); + rb_define_method(Ctimestamp, "minute", timestamp_min, -1); + rb_define_method(Ctimestamp, "second", timestamp_sec, -1); + rb_define_method(Ctimestamp, "fraction", timestamp_fraction, -1); + rb_define_method(Ctimestamp, "year=", timestamp_year, -1); + rb_define_method(Ctimestamp, "month=", timestamp_month, -1); + rb_define_method(Ctimestamp, "day=", timestamp_day, -1); + rb_define_method(Ctimestamp, "hour=", timestamp_hour, -1); + rb_define_method(Ctimestamp, "minute=", timestamp_min, -1); + rb_define_method(Ctimestamp, "second=", timestamp_sec, -1); + rb_define_method(Ctimestamp, "fraction=", timestamp_fraction, -1); + rb_define_method(Ctimestamp, "<=>", timestamp_cmp, 1); + + /* procedure methods */ + rb_define_method(Cproc, "initialize", stmt_proc_init, -1); + rb_define_method(Cproc, "call", stmt_proc_call, -1); + rb_define_method(Cproc, "[]", stmt_proc_call, -1); + rb_attr(Cproc, IDstatement, 1, 0, Qfalse); + rb_attr(Cproc, IDreturn_output_param, 1, 0, Qfalse); +#ifndef HAVE_RB_DEFINE_ALLOC_FUNC + rb_enable_super(Cproc, "call"); + rb_enable_super(Cproc, "[]"); +#endif + + /* constants */ + for (i = 0; o_const[i].name != NULL; i++) { + rb_define_const(Modbc, o_const[i].name, + INT2NUM(o_const[i].value)); + } + for (i = 0; get_info_map[i].name != NULL; i++) { + rb_define_const(Modbc, get_info_map[i].name, + INT2NUM(get_info_map[i].info)); + } + for (i = 0; get_info_bitmap[i].name != NULL; i++) { + rb_define_const(Modbc, get_info_bitmap[i].name, + INT2NUM(get_info_bitmap[i].bits)); + } + for (i = 0; option_map[i].name != NULL; i++) { + rb_define_const(Modbc, option_map[i].name, + INT2NUM(option_map[i].option)); + } + +#ifdef UNICODE + rb_define_const(Modbc, "UTF8", Qtrue); +#ifdef USE_RB_ENC + rb_enc = rb_utf8_encoding(); + rb_encv = rb_enc_from_encoding(rb_enc); +#endif +#else + rb_define_const(Modbc, "UTF8", Qfalse); +#endif + +#ifdef TRACING + if (ruby_verbose) { + tracing = -1; + } +#endif +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * tab-width: 8 + * End: + */ diff --git a/ruby-odbc-0.999991/ext/utf8/extconf.rb b/ruby-odbc-0.999991/ext/utf8/extconf.rb new file mode 100644 index 0000000..26ef8c9 --- /dev/null +++ b/ruby-odbc-0.999991/ext/utf8/extconf.rb @@ -0,0 +1,158 @@ +require 'mkmf' + +if ! defined? PLATFORM + PLATFORM = RUBY_PLATFORM +end + +def have_library_ex(lib, func="main", headers=nil) + checking_for "#{func}() in -l#{lib}" do + libs = append_library($libs, lib) + if !func.nil? && !func.empty? && COMMON_LIBS.include?(lib) + true + elsif try_func(func, libs, headers) + $libs = libs + true + else + false + end + end +end + +def try_func_nolink(func, libs, headers = nil, &b) + headers = cpp_include(headers) + try_compile(<<"SRC", libs, &b) +#{COMMON_HEADERS} +#{headers} +/*top*/ +int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; } +SRC +end + +def have_func_nolink(func, headers = nil, &b) + checking_for "#{func}()" do + if try_func_nolink(func, $libs, headers, &b) + $defs.push(format("-DHAVE_%s", func.upcase)) + true + else + false + end + end +end + +dir_config("odbc") +have_header("version.h") +have_header("ruby/version.h") +have_header("sql.h") || begin + puts "ERROR: sql.h not found" + exit 1 +end +have_header("sqlext.h") || begin + puts "ERROR: sqlext.h not found" + exit 1 +end +testdlopen = enable_config("dlopen", false) +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLTCHAR", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLTCHAR manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLTCHAR" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLLEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLLEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLLEN" +end +begin + if PLATFORM !~ /(mingw|cygwin)/ then + header = "sqltypes.h" + else + header = ["windows.h", "sqltypes.h"] + end + if defined? have_type + have_type("SQLULEN", header) + else + throw + end +rescue + puts "WARNING: please check sqltypes.h for SQLULEN manually," + puts "WARNING: if defined, modify CFLAGS in Makefile to contain" + puts "WARNING: the option -DHAVE_TYPE_SQLULEN" +end +$have_odbcinst_h = have_header("odbcinst.h") + +if PLATFORM =~ /mswin32/ then + if !have_library_ex("odbc32", "SQLAllocConnect", "sql.h") || + !have_library_ex("odbccp32", "SQLConfigDataSource", "odbcinst.h") || + !have_library_ex("odbccp32", "SQLInstallerError", "odbcinst.h") || + !have_library("user32", "CharUpper") then + puts "Can not locate odbc libraries" + exit 1 + end + have_func("SQLConfigDataSourceW", "odbcinst.h") + have_func("SQLWriteFileDSNW", "odbcinst.h") + have_func("SQLReadFileDSNW", "odbcinst.h") + have_func("SQLInstallerError", "odbcinst.h") + have_func("SQLInstallerErrorW", "odbcinst.h") +elsif PLATFORM =~ /(mingw|cygwin)/ then + have_library("odbc32") + have_library("odbccp32") + have_library("user32") + header = ["windows.h", "odbcinst.h"] + have_func("SQLConfigDataSourceW", header) + have_func("SQLWriteFileDSNW", header) + have_func("SQLReadFileDSNW", header) + have_func("SQLInstallerError", header) + have_func("SQLInstallerErrorW", header) +elsif (testdlopen && PLATFORM !~ /(macos|darwin)/ && CONFIG["CC"] =~ /gcc/ && have_func("dlopen", "dlfcn.h") && have_library("dl", "dlopen")) then + $LDFLAGS+=" -Wl,-init -Wl,ruby_odbc_init -Wl,-fini -Wl,ruby_odbc_fini" + $CPPFLAGS+=" -DHAVE_SQLCONFIGDATASOURCE" + $CPPFLAGS+=" -DHAVE_SQLINSTALLERERROR" + $CPPFLAGS+=" -DUSE_DLOPEN_FOR_ODBC_LIBS" + # but test the UNICODE installer functions w/o linking + # in case we need to provide fwd declarations + have_func_nolink("SQLConfigDataSourceW", "odbcinst.h") + have_func_nolink("SQLWriteFileDSNW", "odbcinst.h") + have_func_nolink("SQLReadFileDSNW", "odbcinst.h") + have_func_nolink("SQLInstallerErrorW", "odbcinst.h") +else + $CPPFLAGS+=" -DUNICODE -D_UNICODE" + have_library("odbc", "SQLAllocConnect") || + have_library("iodbc", "SQLAllocConnect") + ($have_odbcinst_h && + have_library("odbcinst", "SQLConfigDataSource")) || + ($have_odbcinst_h && + have_library("iodbcinst", "SQLConfigDataSource")) + $have_odbcinst_h && + have_func("SQLConfigDataSourceW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLWriteFileDSNW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLReadFileDSNW", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLInstallerError", "odbcinst.h") + $have_odbcinst_h && + have_func("SQLInstallerErrorW", "odbcinst.h") +end + +create_makefile("odbc_utf8") diff --git a/ruby-odbc-0.999991/ext/utf8/init.c b/ruby-odbc-0.999991/ext/utf8/init.c new file mode 100644 index 0000000..1bf2e6b --- /dev/null +++ b/ruby-odbc-0.999991/ext/utf8/init.c @@ -0,0 +1,12 @@ +/* + * Part of ODBC-Ruby binding + * Copyright (c) 2006-2007 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: init.c,v 1.2 2007/04/07 09:39:08 chw Exp chw $ + */ + +#include "../init.c" diff --git a/ruby-odbc-0.999991/ext/utf8/odbc.c b/ruby-odbc-0.999991/ext/utf8/odbc.c new file mode 100644 index 0000000..a222b9a --- /dev/null +++ b/ruby-odbc-0.999991/ext/utf8/odbc.c @@ -0,0 +1,17 @@ +/* + * ODBC-Ruby binding + * Copyright (c) 2001-2011 Christian Werner + * + * See the file "COPYING" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + * + * $Id: odbc.c,v 1.6 2011/01/15 08:02:55 chw Exp chw $ + */ + +#undef UNICODE +#undef _UNICODE +#define UNICODE +#define _UNICODE + +#include "../odbc.c" diff --git a/ruby-odbc-0.999991/lib/cqgen.rb b/ruby-odbc-0.999991/lib/cqgen.rb new file mode 100644 index 0000000..ef2e79f --- /dev/null +++ b/ruby-odbc-0.999991/lib/cqgen.rb @@ -0,0 +1,561 @@ +#!/usr/bin/env ruby +# +# cqgen.rb - Poor man's C code generator to automatically +# make access wrappers for ODBC datasources. +# +# ------------------------------------------------------------------- +# This file is part of "ODBC-Ruby binding" +# +# Copyright (c) 2006 Christian Werner +# +# See the file "COPYING" for information on usage +# and redistribution of this file and for a +# DISCLAIMER OF ALL WARRANTIES. +# ------------------------------------------------------------------- +# +# Pre-requisites: +# +# * Ruby 1.6 or 1.8 +# * Ruby/ODBC >= 0.996 +# * Acesss to a ODBC datasource with properly +# loaded schema for the wrappers to generate +# +# ------------------------------------------------------------------- +# +# Functions in this module +# +# cqgen DSName, UID, PWD, CFName, SQL, [ParmTypes, ParmNames] +# +# DSName: name of datasource to connect +# UID: user name on datasource +# PWD: password on datasource +# CFName: name for C function +# SQL: SQL text of one query yielding +# one result set +# ParmTypes: (optional, array) types for +# query parameters +# ParmNames: (optional, array) names for +# query parameters +# +# use this function to make C code for +# SELECT/UPDATE/DELETE/INSERT SQL statements. +# The "ParmTypes" is useful since many ODBC drivers +# cannot return the proper types in SQLDescribeParam(). +# +# cqgen_test +# append test functions and skeleton test main() +# to end of C file +# +# +# cqgen_output c_name, h_name +# write C source code, 'c_name' for functions, +# 'h_name' for optional header file +# +# ------------------------------------------------------------------- +# +# Specification file sample +# +# ---BOF--- +# require 'cqgen' +# # simple query +# cqgen 'bla', '', '', "GetResults", %q{ +# select * from results +# } +# # query with parameters using last DSN +# cqgen nil, nil, nil, "GetResultsGt", %q{ +# select * from results where funfactor > ? +# } +# # update with parameters using other DSN +# cqgen 'bla2', '', '', "IncrFunFactor", %q{ +# update results set funfactor = ? where funfactor < ? +# }, [ ODBC::SQL_INTEGER, ODBC::SQL_INTEGER ], +# [ "newfunfactor", "maxfunfactor" ] +# cqgen_test +# cqgen_output 'bla_wrap.c' +# ---EOF--- +# +# Run this file with a command line like +# +# $ ruby sample-file +# $ cc -c bla_wrap.c +# or +# $ cc -o bla_wrap_test bla_wrap.c -DTEST_cqgen_all -lodbc +# +# Depending on the SQL text, cqgen writes one or more +# C functions and zero or one structure typedefs. +# The structure typedefs are merged when possible, +# thus you should write the least specific queries +# on top of the specification file. +# +# In the sample: +# +# * function GetResults_init to initiate the query +# * function GetResults to retrieve one result row +# * function GetResults_deinit to release resources +# associated with the query +# * struct RESULT_GetResults for representation of +# one result row +# +# * function GetResultsGt_init with one parameter par1 +# * macro GetResultsGt aliased to GetResults +# (function reused from 1st query) +# * macro GetResultsGt_deinit aliased to GetResults_deinit +# (function reused from 1st query) +# * struct RESULT_GetResults for representation of +# one result row (struct name reused from 1st query) +# +# * function IncrFunFactor with two parameters named +# "newfunfactor" and "maxfunfactor" and an SQLINTEGER +# reference for the number of affected rows +# +# ------------------------------------------------------------------- + +require 'odbc' + +$cqgen_dsn=nil +$cqgen_uid=nil +$cqgen_pwd=nil +$cqgen_simple_fns=Hash.new +$cqgen_stypes=Hash.new +$cqgen_h1='' +$cqgen_h2='' +$cqgen_c='' +$cqgen_ct1='' +$cqgen_ct2='' + +def cqgen(dsn, uid, pwd, name, sql, parmtypes=nil, parmnames=nil) + dsn=$cqgen_dsn if dsn.nil? + uid=$cqgen_uid if uid.nil? + pwd=$cqgen_pwd if pwd.nil? + begin + conn=ODBC::connect(dsn, uid, pwd) + $cqgen_dsn=dsn + $cqgen_uid=uid + $cqgen_pwd=pwd + rescue Exception => err + $stderr.puts("connect for #{name} failed:\n") + $stderr.puts("\t#{err}\n") + return + end + sql = sql.strip + sql = sql.gsub(/[\r\n]+/, " ") + if sql.downcase =~ /where/ + sql0=sql + " and 1=0" + else + sql0=sql + " where 1=0" + end + begin + stmt=conn.prepare(sql0) + if stmt.ncols < 1 + stmt.execute + end + rescue Exception => err + $stderr.puts("SQL error for #{name}:\n") + $stderr.puts("\t#{err}\n") + return + end + if stmt.ncols > 0 + fhead="SQLRETURN\n" + name + "_init(SQLHDBC hdbc, SQLHSTMT *hstmtRet" + fhead2="SQLRETURN\n" + name + "(SQLHSTMT hstmt" + flvars2="{\n SQLRETURN ret;\n INTERNAL_STMT *istmt;\n" + fbody2="\n istmt = (INTERNAL_STMT *) hstmt;\n" + fhead3="SQLRETURN\n" + name + "_deinit(SQLHSTMT hstmt)\n" + flvars3="{\n INTERNAL_STMT *istmt;\n" + fbody3="\n istmt = (INTERNAL_STMT *) hstmt;\n" + fbody3+=" if (istmt != NULL) {\n" + fbody3+="\tSQLFreeStmt(istmt->hstmt, SQL_DROP);\n" + fbody3+="\tfree(istmt);\n" + fbody3+=" }\n return SQL_SUCCESS;\n}\n\n" + else + fhead="SQLRETURN\n" + name + "(SQLHDBC hdbc" + fhead2="" + flvars2="" + fbody2="" + fhead3="" + flvars3="" + fbody3="" + end + flvars="{\n SQLRETURN ret;\n" + if stmt.ncols < 1 + flvars+=" SQLHSTMT hstmt;\n" + end + fbody="" + flvarst="{\n SQLRETURN ret;\n" + fheadt="SQLRETURN\n" + name + "_test(SQLHDBC hdbc" + fbodyt="" + if stmt.ncols > 0 + flvars+=" INTERNAL_STMT *istmt;\n SQLHSTMT hstmt;\n" + fbody+=" *hstmtRet = hstmt = SQL_NULL_HSTMT;\n" + fbody+=" ret = SQLAllocStmt(hdbc, &hstmt);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody+=" istmt = malloc(sizeof (INTERNAL_STMT));\n" + fbody+=" if (istmt == NULL) {\n\tSQLFreeStmt(hstmt, SQL_DROP);\n" + fbody+="\treturn SQL_ERROR;\n }\n" + fbody+=" istmt->hstmt = hstmt;\n" + fbody+=" istmt->result = NULL;\n" + fbody+=" *hstmtRet = (SQLHSTMT) istmt;\n" + flvarst+=" SQLHSTMT hstmt;\n" + fbodyt+=" ret = #{name}_init(hdbc, &hstmt" + else + fbodyt+=" ret = #{name}(hdbc" + end + pnum=1 + stmt.parameters.each do |par| + if !parmtypes.nil? + ptype=parmtypes[pnum-1] + end + if ptype.nil? + ptype = par.type + end + if !parmnames.nil? + pname=parmnames[pnum-1] + end + if pname.nil? + pname="par#{pnum}" + end + case ptype + when ODBC::SQL_INTEGER + pdef=", SQLINTEGER *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLINTEGER);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_LONG, SQL_INTEGER, #{par.precision}, #{par.scale}," + when ODBC::SQL_SMALLINT + pdef=", SQLSMALLINT *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLSMALLINT);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_SHORT, SQL_SMALLINT, #{par.precision}, #{par.scale}," + when ODBC::SQL_FLOAT + pdef=", SQLDOUBLE *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLDOUBLE);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_DOUBLE, SQL_FLOAT, #{par.precision}, #{par.scale}," + when ODBC::SQL_DOUBLE + pdef=", SQLDOUBLE *#{pname}" + fbody+=" #{pname}_len = sizeof (SQLDOUBLE);\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_DOUBLE, SQL_DOUBLE, #{par.precision}, #{par.scale}," + else + pdef=", SQLCHAR *#{pname}" + fbody+=" #{pname}_len = #{pname} ? strlen(#{pname}) : SQL_NULL_DATA;\n"; + fbody+=" ret = SQLBindParameter(hstmt, #{pnum}, SQL_PARAM_INPUT," + fbody+=" SQL_C_CHAR, SQL_CHAR, #{par.precision}, #{par.scale}," + end + fbody+=" #{pname}, #{pname}_len, &#{pname}_len);\n" + fhead+=pdef + fheadt+=pdef + fbodyt+=", #{pname}" + flvars+=" SQLINTEGER #{pname}_len;\n" + if stmt.ncols > 0 + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + else + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + end + pnum+=1 + end + fheadt+=")\n" + if stmt.ncols < 1 + fbodyt+= ", &rowCount" + end + fbodyt+=");\n" + if stmt.ncols > 0 + fbodyt+=" if (!SQL_SUCCEEDED(ret)) {\n" + fbodyt+="\t#{name}_deinit(hstmt);\n\treturn ret;\n }\n" + end + struc="" + istruc="" + if stmt.ncols > 0 + if pnum<=1 + $cqgen_simple_fns["#{name}_test"]=1 + end + fbody+=" ret = SQLPrepare(hstmt, \"#{sql}\", SQL_NTS);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody+=" ret = SQLExecute(hstmt);\n" + fbody+=" return ret;\n}\n\n" + struc+="typedef struct {\n" + fbody2+=" hstmt = istmt->hstmt;\n" + fbody2+=" if (result == istmt->result) {\n" + fbody2+="\tgoto doexec;\n }\n" + fbody2+=" istmt->result = NULL;\n" + fbodyt+=" do {\n" + fbodyt+="\tret = #{name}(hstmt, &result);\n" + fbodyt+="\tif (!SQL_SUCCEEDED(ret)) {\n\t break;\n\t}\n" + cols = stmt.columns(1) + coltable = Hash.new + cols.each do |col| + coltable[col.table => 1] + end + cnum=1 + cols.each do |col| + if coltable.size > 1 + cname=col.table+"_"+col.name + else + cname=col.name + end + case col.type + when ODBC::SQL_INTEGER + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLINTEGER #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_LONG," + fbody2+=" &result->#{cname}, sizeof (SQL_C_LONG)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_SMALLINT + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLSMALLINT #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_SMALLINT," + fbody2+=" &result->#{cname}, sizeof (SQL_C_SMALLINT)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_FLOAT + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLDOUBLE #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_DOUBLE," + fbody2+=" &result->#{cname}, sizeof (SQLDOUBLE)," + fbody2+=" &result->#{cname}_len);\n" + when ODBC::SQL_DOUBLE + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLDOUBLE #{cname};\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_DOUBLE," + fbody2+=" &result->#{cname}, sizeof (SQL_C_DOUBLE)," + fbody2+=" &result->#{cname}_len);\n" + else + istruc+=" SQLLEN #{cname}_len;\n" + istruc+=" SQLCHAR #{cname}[#{col.length}];\n" + fbody2+=" ret = SQLBindCol(hstmt, #{cnum}, SQL_C_CHAR," + fbody2+=" result->#{cname}, #{col.length}," + fbody2+=" &result->#{cname}_len);\n" + end + fbody2+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + cnum+=1; + end + sname=$cqgen_stypes[istruc] + if sname.nil? + sname="RESULT_#{name}" + $cqgen_stypes[istruc]=sname + addstruct=true + else + addstruct=false + end + struc+="#{istruc}} #{sname};\n\n" + flvarst+=" #{sname} result;\n\n" + if addstruct + fhead2+=", #{sname} *result" + else + fbody2="" + flvars2="" + fbody3="" + flvars3="" + fname=sname.gsub(/^RESULT_/, "") + fhead2="#define\t\t#{name}\t#{fname}\n" + fhead3="#define\t\t#{name}_deinit\t#{fname}_deinit\n" + end + cnum=1 + cols.each do |col| + if coltable.size > 1 + cname=col.table+"_"+col.name + else + cname=col.name + end + case col.type + when ODBC::SQL_INTEGER + fmt='%ld' + when ODBC::SQL_SMALLINT + fmt='%d' + when ODBC::SQL_FLOAT + fmt='%g' + when ODBC::SQL_DOUBLE + fmt='%g' + else + fmt='%s' + end + fbodyt+="\tif (result.#{cname}_len != SQL_NULL_DATA) {\n" + fbodyt+="\t printf(\"#{sname}.#{cname}=#{fmt}\\n\",\n" + fbodyt+="\t\t result.#{cname});\n" + fbodyt+="\t} else {\n" + fbodyt+="\t printf(\"#{sname}.#{cname}=NULL\\n\");\n" + fbodyt+="\t}\n" + cnum+=1; + end + fbodyt+=" } while (SQL_SUCCEEDED(ret));\n" + fbodyt+=" #{name}_deinit(hstmt);\n" + else + fhead+=", SQLINTEGER *rowCountRet" + fbody=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + fbody + fbody=" ret = SQLAllocStmt(hdbc, &hstmt);\n" + fbody + fbody=" *rowCountRet = 0;\n" + fbody + fbody+=" ret = SQLExecDirect(hstmt, " + fbody+='"'+sql+'"' + fbody+=", SQL_NTS);\n" + fbody+=" if (!SQL_SUCCEEDED(ret)) {\n\tgoto done;\n }\n" + fbody+=" ret = SQLRowCount(hstmt, rowCountRet);\ndone:\n" + fbody+=" SQLFreeStmt(hstmt, SQL_DROP);\n return ret;\n}\n\n" + flvarst+=" SQLINTEGER rowCount;\n\n" + fbodyt+=" printf(\"#{name}_test rowCount=%ld\\n\", rowCount);\n" + end + if fbodyt.length > 0 + fbodyt+=" return ret;\n" + fbodyt+="}\n" + end + fhead+=")\n" + if fbody2.length > 0 + fhead2+=")\n" + fbody2+="doexec:\n" + fbody2+=" ret = SQLFetch(hstmt);\n" + fbody2+=" if (!SQL_SUCCEEDED(ret)) {\n\treturn ret;\n }\n" + fbody2+=" istmt->result = result;\n" + fbody2+=" return ret;\n}\n\n" + end + flvars+="\n" + stmt.drop + conn.disconnect + sql = sql.gsub(/[ \t]+/, " ") + sql = sql.gsub(/\/\*/, "") + sql = sql.gsub(/\*\//, "") + $cqgen_h1+="/* #{sql} */\n" if addstruct + $cqgen_h1+=struc if addstruct + $cqgen_h2+="/* #{sql} */\n" + tmp=fhead.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + if fhead2.length > 0 + tmp=fhead2.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + end + if fhead3.length > 0 + tmp=fhead3.gsub(/SQLRETURN\n/, "SQLRETURN\t"); + $cqgen_h2+=tmp.gsub(/\)\n/, ");\n") + end + $cqgen_h2+="\n" + $cqgen_c+=fhead + $cqgen_c+=flvars + $cqgen_c+=fbody + $cqgen_c+=fhead2 unless fhead2 =~ /^#define/ + $cqgen_c+=flvars2 + $cqgen_c+=fbody2 + $cqgen_c+=fhead3 unless fhead3 =~ /^#define/ + $cqgen_c+=flvars3 + $cqgen_c+=fbody3 + $cqgen_ct1+="#if defined(TEST_#{name}) || defined(TEST_cqgen_all)\n" + $cqgen_ct1+=fheadt + $cqgen_ct1+=flvarst + $cqgen_ct1+=fbodyt + $cqgen_ct1+="#endif\n\n" +end + +def cqgen_test + $cggen_dsn="" if $cqgen_dsn.nil? + $cggen_uid="" if $cqgen_uid.nil? + $cggen_pwd="" if $cqgen_pwd.nil? + test_calls="" + $cqgen_simple_fns.each_key do |name| + test_calls+=" printf(\"=== #{name} ===\\n\");\n" + test_calls+=" #{name}(hdbc);\n" + end + $cqgen_ct2=$cqgen_ct1 + $cqgen_ct2+=%Q{ +#ifdef TEST_cqgen_all +int +main(int argc, char **argv) +{ + SQLHENV henv; + SQLHDBC hdbc; + SQLRETURN ret; + + ret = SQLAllocEnv(&henv); + if (!SQL_SUCCEEDED(ret)) { + exit(1); + } + ret = SQLAllocConnect(henv, &hdbc); + if (!SQL_SUCCEEDED(ret)) { + exit(2); + } + ret = SQLConnect(hdbc, + \"#{$cqgen_dsn}\", SQL_NTS, + \"#{$cqgen_uid}\", SQL_NTS, + \"#{$cqgen_pwd}\", SQL_NTS); + if (!SQL_SUCCEEDED(ret)) { + exit(3); + } + + /* ADD YOUR TEST CALLS HERE */ +#{test_calls} + SQLDisconnect(hdbc); + SQLFreeConnect(hdbc); + SQLFreeEnv(henv); + return 0; +} +#endif + +} +end + +def cqgen_output(c_name=nil, h_name=nil) + h='' + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n" + h+="#include \n\n" + h+=$cqgen_h1 + h+="\n" + h+=$cqgen_h2 + if c_name.nil? + cf=$stdout + ctoclose=false + else + cf=File::open(c_name, 'w') + ctoclose=true + end + if h_name.nil? + hf=cf + htoclose=false + else + hf=File::open(h_name, 'w') + htoclose=true + hb=File::basename(h_name).gsub(/\W/, "_") + h="#ifndef _#{hb}\n#define _#{hb}\n\n#{h}\n#endif /* _#{hb} */\n" + end + hf.print(h) + c='' + if !h_name.nil? && (hf != cf) + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n" + c+="#include \n\n" + c+="#include \"#{h_name}\"\n\n" + end + c+=%Q{ +typedef struct { + SQLHSTMT hstmt; + void *result; +} INTERNAL_STMT; + + +} + c+=$cqgen_c + c+=$cqgen_ct2 + cf.print(c) + hf.close if htoclose + cf.close if ctoclose + $cqgen_h1='' + $cqgen_h2='' + $cqgen_c='' + $cqgen_ct1='' + $cqgen_ct2='' + $cqgen_simple_fns.clear + $cqgen_stypes.clear +end + +# Run this code only when the file is the main program +if $0 == __FILE__ + files=ARGV.flatten + ARGV.clear + files.each do |f| + begin + load f + rescue Exception => err + $stderr.puts("in file #{f}:\n") + $stderr.puts("\t#{err}\n") + end + end +end diff --git a/ruby-odbc-0.999991/ruby-odbc.gemspec b/ruby-odbc-0.999991/ruby-odbc.gemspec new file mode 100644 index 0000000..7ee221c --- /dev/null +++ b/ruby-odbc-0.999991/ruby-odbc.gemspec @@ -0,0 +1,16 @@ +require 'date' +spec = Gem::Specification.new do |s| + s.name = "ruby-odbc" + s.version = "0.999991" + s.date = Date.today.to_s + s.author = "Christian Werner" + s.email = "chw @nospam@ ch-werner.de" + s.summary = "ODBC binding for Ruby" + s.homepage = "http://www.ch-werner.de/rubyodbc" + s.files = Dir.glob("**/*") + s.require_paths << 'lib' + s.test_files = Dir.glob('tests/*.rb') + s.has_rdoc = false + s.extra_rdoc_files = ["README", "COPYING", "ChangeLog", "GPL", "doc/odbc.html"] + s.extensions = ["ext/extconf.rb", "ext/utf8/extconf.rb"] +end diff --git a/ruby-odbc-0.999991/test/00connect.rb b/ruby-odbc-0.999991/test/00connect.rb new file mode 100644 index 0000000..54ca78d --- /dev/null +++ b/ruby-odbc-0.999991/test/00connect.rb @@ -0,0 +1 @@ +$c = ODBC.connect($dsn, $uid, $pwd) diff --git a/ruby-odbc-0.999991/test/10create_table.rb b/ruby-odbc-0.999991/test/10create_table.rb new file mode 100644 index 0000000..c024596 --- /dev/null +++ b/ruby-odbc-0.999991/test/10create_table.rb @@ -0,0 +1 @@ +$c.run("create table test (id int not null, str varchar(32) not null)") diff --git a/ruby-odbc-0.999991/test/20insert.rb b/ruby-odbc-0.999991/test/20insert.rb new file mode 100644 index 0000000..cfb6cc2 --- /dev/null +++ b/ruby-odbc-0.999991/test/20insert.rb @@ -0,0 +1,6 @@ +$q = $c.run("insert into test (id, str) values (1, 'foo')") +$q.run("insert into test (id, str) values (2, 'bar')") + +$p = $c.proc("insert into test (id, str) values (?, ?)") {} +$p.call(3, "FOO") +$p[4, "BAR"] diff --git a/ruby-odbc-0.999991/test/30select.rb b/ruby-odbc-0.999991/test/30select.rb new file mode 100644 index 0000000..5065cfa --- /dev/null +++ b/ruby-odbc-0.999991/test/30select.rb @@ -0,0 +1,69 @@ +$q = $c.prepare("select id,str from test order by id") + +if $q.column(0).name.upcase != "ID" then raise "fetch failed" end +if $q.column(1).name.upcase != "STR" then raise "fetch failed" end + +$q.execute +if $q.fetch != [1, "foo"] then raise "fetch: failed" end +if $q.fetch != [2, "bar"] then raise "fetch: failed" end +if $q.fetch != [3, "FOO"] then raise "fetch: failed" end +if $q.fetch != [4, "BAR"] then raise "fetch: failed" end +if $q.fetch != nil then raise "fetch: failed" end +$q.close + +if $q.execute.entries != [[1, "foo"], [2, "bar"], [3, "FOO"], [4, "BAR"]] then + raise "fetch: failed" +end +$q.close + +if $q.execute.fetch_all != [[1, "foo"], [2, "bar"], [3, "FOO"], [4, "BAR"]] then + raise "fetch: failed" +end +$q.close + +$q.execute +if $q.fetch_many(2) != [[1, "foo"], [2, "bar"]] then raise "fetch: failed" end +if $q.fetch_many(3) != [[3, "FOO"], [4, "BAR"]] then raise "fetch: failed" end +if $q.fetch_many(99) != nil then raise "fetch: failed" end +$q.close + +a = [] +$q.execute {|r| a=r.entries} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(true) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Symbol) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Symbol,:table_names=>true) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:String,:table_names=>false) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Fixnum,:table_names=>false) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + diff --git a/ruby-odbc-0.999991/test/40update.rb b/ruby-odbc-0.999991/test/40update.rb new file mode 100644 index 0000000..8fd9a67 --- /dev/null +++ b/ruby-odbc-0.999991/test/40update.rb @@ -0,0 +1,4 @@ +$q = $c.run("update test set id=0, str='hoge'") +if $q.nrows != 4 then + $stderr.print "update row count: expected 4, got ", $q.nrows, "\n" +end diff --git a/ruby-odbc-0.999991/test/45delete.rb b/ruby-odbc-0.999991/test/45delete.rb new file mode 100644 index 0000000..281a2de --- /dev/null +++ b/ruby-odbc-0.999991/test/45delete.rb @@ -0,0 +1,8 @@ +$count = $c.do("delete from test where 1 = 1") +if $count != 4 + $stderr.print "delete row count: expected 4, got ", $count, "\n" +end +$count = $c.do("delete from test where 1 = 1") +if $count != 0 + $stderr.print "delete row count: expected 0, got ", $count, "\n" +end diff --git a/ruby-odbc-0.999991/test/50drop_table.rb b/ruby-odbc-0.999991/test/50drop_table.rb new file mode 100644 index 0000000..1867772 --- /dev/null +++ b/ruby-odbc-0.999991/test/50drop_table.rb @@ -0,0 +1,3 @@ +# drop all statements, otherwise table might be locked for drop. +$c.drop_all +$c.do("drop table test") diff --git a/ruby-odbc-0.999991/test/70close.rb b/ruby-odbc-0.999991/test/70close.rb new file mode 100644 index 0000000..35b5f56 --- /dev/null +++ b/ruby-odbc-0.999991/test/70close.rb @@ -0,0 +1 @@ +$c.disconnect diff --git a/ruby-odbc-0.999991/test/test.rb b/ruby-odbc-0.999991/test/test.rb new file mode 100644 index 0000000..45ab8fd --- /dev/null +++ b/ruby-odbc-0.999991/test/test.rb @@ -0,0 +1,32 @@ +# $Id: test.rb,v 1.8 2015/03/25 10:53:59 chw Exp chw $ +# +# Execute in ruby-odbc top directory. +# +# ruby test.rb "mysql-DSN" "mysql-user" "mysql-password" +# +# Test creates and deletes table "test" in that DSN. + +require 'odbc' + +$dsn = ARGV.shift +$uid = ARGV.shift +$pwd = ARGV.shift + +begin + Dir.glob("[0-9]*.rb").sort.each do |f| + f =~ /\d+(.*)\.rb$/ + print $1 + "."*(20-$1.length) + $stdout.flush + load f + print "ok\n" + end +ensure + begin + $c.drop_all unless $c.class != ODBC::Database + rescue + end + begin + ODBC.connect($dsn, $uid, $pwd).do("drop table test") + rescue + end +end diff --git a/ruby-odbc-0.999991/test/utf8/test.rb b/ruby-odbc-0.999991/test/utf8/test.rb new file mode 100644 index 0000000..f5dee39 --- /dev/null +++ b/ruby-odbc-0.999991/test/utf8/test.rb @@ -0,0 +1,32 @@ +# $Id: test.rb,v 1.4 2015/03/25 10:54:18 chw Exp chw $ +# +# Execute in ruby-odbc utf8 directory. +# +# ruby test.rb "mysql-DSN" "mysql-user" "mysql-password" +# +# Test creates and deletes table "test" in that DSN. + +require 'odbc_utf8' + +$dsn = ARGV.shift +$uid = ARGV.shift +$pwd = ARGV.shift + +begin + Dir.glob("../[0-9]*.rb").sort.each do |f| + f =~ /^..\/\d+(.*)\.rb$/ + print $1 + "."*(20-$1.length) + $stdout.flush + load f + print "ok\n" + end +ensure + begin + $c.drop_all unless $c.class != ODBC::Database + rescue + end + begin + ODBC.connect($dsn, $uid, $pwd).do("drop table test") + rescue + end +end diff --git a/ruby-odbc.gemspec b/ruby-odbc.gemspec new file mode 100644 index 0000000..7ee221c --- /dev/null +++ b/ruby-odbc.gemspec @@ -0,0 +1,16 @@ +require 'date' +spec = Gem::Specification.new do |s| + s.name = "ruby-odbc" + s.version = "0.999991" + s.date = Date.today.to_s + s.author = "Christian Werner" + s.email = "chw @nospam@ ch-werner.de" + s.summary = "ODBC binding for Ruby" + s.homepage = "http://www.ch-werner.de/rubyodbc" + s.files = Dir.glob("**/*") + s.require_paths << 'lib' + s.test_files = Dir.glob('tests/*.rb') + s.has_rdoc = false + s.extra_rdoc_files = ["README", "COPYING", "ChangeLog", "GPL", "doc/odbc.html"] + s.extensions = ["ext/extconf.rb", "ext/utf8/extconf.rb"] +end diff --git a/test/00connect.rb b/test/00connect.rb new file mode 100644 index 0000000..54ca78d --- /dev/null +++ b/test/00connect.rb @@ -0,0 +1 @@ +$c = ODBC.connect($dsn, $uid, $pwd) diff --git a/test/10create_table.rb b/test/10create_table.rb new file mode 100644 index 0000000..c024596 --- /dev/null +++ b/test/10create_table.rb @@ -0,0 +1 @@ +$c.run("create table test (id int not null, str varchar(32) not null)") diff --git a/test/20insert.rb b/test/20insert.rb new file mode 100644 index 0000000..cfb6cc2 --- /dev/null +++ b/test/20insert.rb @@ -0,0 +1,6 @@ +$q = $c.run("insert into test (id, str) values (1, 'foo')") +$q.run("insert into test (id, str) values (2, 'bar')") + +$p = $c.proc("insert into test (id, str) values (?, ?)") {} +$p.call(3, "FOO") +$p[4, "BAR"] diff --git a/test/30select.rb b/test/30select.rb new file mode 100644 index 0000000..5065cfa --- /dev/null +++ b/test/30select.rb @@ -0,0 +1,69 @@ +$q = $c.prepare("select id,str from test order by id") + +if $q.column(0).name.upcase != "ID" then raise "fetch failed" end +if $q.column(1).name.upcase != "STR" then raise "fetch failed" end + +$q.execute +if $q.fetch != [1, "foo"] then raise "fetch: failed" end +if $q.fetch != [2, "bar"] then raise "fetch: failed" end +if $q.fetch != [3, "FOO"] then raise "fetch: failed" end +if $q.fetch != [4, "BAR"] then raise "fetch: failed" end +if $q.fetch != nil then raise "fetch: failed" end +$q.close + +if $q.execute.entries != [[1, "foo"], [2, "bar"], [3, "FOO"], [4, "BAR"]] then + raise "fetch: failed" +end +$q.close + +if $q.execute.fetch_all != [[1, "foo"], [2, "bar"], [3, "FOO"], [4, "BAR"]] then + raise "fetch: failed" +end +$q.close + +$q.execute +if $q.fetch_many(2) != [[1, "foo"], [2, "bar"]] then raise "fetch: failed" end +if $q.fetch_many(3) != [[3, "FOO"], [4, "BAR"]] then raise "fetch: failed" end +if $q.fetch_many(99) != nil then raise "fetch: failed" end +$q.close + +a = [] +$q.execute {|r| a=r.entries} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(true) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Symbol) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Symbol,:table_names=>true) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:String,:table_names=>false) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + +a = [] +$q.execute.each_hash(:key=>:Fixnum,:table_names=>false) {|r| a.push(r)} +if a.size != 4 then raise "fetch: failed" end +$q.close + diff --git a/test/40update.rb b/test/40update.rb new file mode 100644 index 0000000..8fd9a67 --- /dev/null +++ b/test/40update.rb @@ -0,0 +1,4 @@ +$q = $c.run("update test set id=0, str='hoge'") +if $q.nrows != 4 then + $stderr.print "update row count: expected 4, got ", $q.nrows, "\n" +end diff --git a/test/45delete.rb b/test/45delete.rb new file mode 100644 index 0000000..281a2de --- /dev/null +++ b/test/45delete.rb @@ -0,0 +1,8 @@ +$count = $c.do("delete from test where 1 = 1") +if $count != 4 + $stderr.print "delete row count: expected 4, got ", $count, "\n" +end +$count = $c.do("delete from test where 1 = 1") +if $count != 0 + $stderr.print "delete row count: expected 0, got ", $count, "\n" +end diff --git a/test/50drop_table.rb b/test/50drop_table.rb new file mode 100644 index 0000000..1867772 --- /dev/null +++ b/test/50drop_table.rb @@ -0,0 +1,3 @@ +# drop all statements, otherwise table might be locked for drop. +$c.drop_all +$c.do("drop table test") diff --git a/test/70close.rb b/test/70close.rb new file mode 100644 index 0000000..35b5f56 --- /dev/null +++ b/test/70close.rb @@ -0,0 +1 @@ +$c.disconnect diff --git a/test/test.rb b/test/test.rb new file mode 100644 index 0000000..45ab8fd --- /dev/null +++ b/test/test.rb @@ -0,0 +1,32 @@ +# $Id: test.rb,v 1.8 2015/03/25 10:53:59 chw Exp chw $ +# +# Execute in ruby-odbc top directory. +# +# ruby test.rb "mysql-DSN" "mysql-user" "mysql-password" +# +# Test creates and deletes table "test" in that DSN. + +require 'odbc' + +$dsn = ARGV.shift +$uid = ARGV.shift +$pwd = ARGV.shift + +begin + Dir.glob("[0-9]*.rb").sort.each do |f| + f =~ /\d+(.*)\.rb$/ + print $1 + "."*(20-$1.length) + $stdout.flush + load f + print "ok\n" + end +ensure + begin + $c.drop_all unless $c.class != ODBC::Database + rescue + end + begin + ODBC.connect($dsn, $uid, $pwd).do("drop table test") + rescue + end +end diff --git a/test/utf8/test.rb b/test/utf8/test.rb new file mode 100644 index 0000000..f5dee39 --- /dev/null +++ b/test/utf8/test.rb @@ -0,0 +1,32 @@ +# $Id: test.rb,v 1.4 2015/03/25 10:54:18 chw Exp chw $ +# +# Execute in ruby-odbc utf8 directory. +# +# ruby test.rb "mysql-DSN" "mysql-user" "mysql-password" +# +# Test creates and deletes table "test" in that DSN. + +require 'odbc_utf8' + +$dsn = ARGV.shift +$uid = ARGV.shift +$pwd = ARGV.shift + +begin + Dir.glob("../[0-9]*.rb").sort.each do |f| + f =~ /^..\/\d+(.*)\.rb$/ + print $1 + "."*(20-$1.length) + $stdout.flush + load f + print "ok\n" + end +ensure + begin + $c.drop_all unless $c.class != ODBC::Database + rescue + end + begin + ODBC.connect($dsn, $uid, $pwd).do("drop table test") + rescue + end +end