diff --git a/Changelog b/Changelog deleted file mode 100644 index 186f9163be42c803b68f4e49239d6c2b571b5b8d..0000000000000000000000000000000000000000 --- a/Changelog +++ /dev/null @@ -1,38 +0,0 @@ -* Tue 30 Jun 2009 22:14:38 CEST -Added test cases for the standalone driver (ruby unit test). - -* Thu 25 Jun 2009 17:31:02 CEST -Fixed a major bug that resulted in data corruption; -MAPI implementation code cleanup and bug fixes; -Support to ruby 1.9; -Both the standalone driver and activerecord now support transactions; nested transactions -are simulated via savepoints in activerecord. -Added a Rakefile and script to run activerecord unit test suite. -Type conversion in the standalone driver is now performed after data has been retrieved and can be executed on single fields. - -* Mon 25 May 2009 17:52:01 CEST -Imported last week changes (protocol 9 support, parametrized the connection options in the activerecord adapter, fixed a bug in the auth protocol v8). -Fixed a bug in the mapi protocol that resulted in data loss (wrong handling of TRANSACTIONS). -Began to port the activerecord test suite to monetdb (not all test cases can be performed). -Removed an unneeded file ('lib/MonetDBStatement.rb') from the gemspec of the standalone driver (the feature will be moved to HEAD). -Began to port the driver to ruby 1.9. -Removed *.gem files from cvs. - -* Mon 18 May 2009 15:22:31 CEST -Fixed bugs that prevented the correct working of activerecords' migration; -The activerecord connector now supports insertion, update and alter table operations; -Type casting is working in activerecord; -Added a rubygem and rakefile for activerecord-monetdb-adapter; -Added a new usage example for activerecord to the README file; -Added an example directory to the cvs tree; -The driver now correctly works with merovingian. - - -* Sat 9 May 2009 15:58:36 CEST -Fixed bugs with the query processing in the standalone driver; -Added INSERT and UPDATE methods in the activerecord connector. - -* Thu 7 May 2009 17:03:01 CEST - -Added a check against the protocol version during authentication; -Imported the activerecord code (available under adapter/). diff --git a/README b/README deleted file mode 100644 index 4143b64561faa30531f831aab92ca74079468429..0000000000000000000000000000000000000000 --- a/README +++ /dev/null @@ -1,154 +0,0 @@ -== Standalone driver == -This directory contains the a ruby interface to monetdb5 -written in pure ruby. - -lib/MonetDB.rb -lib/MonetDBConnection.rb -lib/MonetDBStatements.rb -lib/MonetDBData.rb -lib/MonetDBExceptions.rb -lib/hasher.rb -lib/demo.rb: demo application how to interact with the database - -ruby-monetdb-sql-0.1.gemspec: make file for rubygems - -doc/: rubydoc in HTML format - -== Installation == - -The standalone monetdb driver can be installed using the RubyGems Package Manager. - -First build a gem file starting from the gemspec configuration: - -$ gem build ruby-monetdb-sql-0.1.gemspec - -Then install with the command: - -$ gem install ruby-monetdb-sql-0.1.gem - -== Usage == -To use the standalone driver import the 'MonetDB' class and 'rubygems' (in case you installed it using gems). - -A typical sequence of events is as follows: -Invoke query using the database handle to send the statement to the server and get back a result set object. - -A result set object has methods for fetching rows, moving around in the result set, obtaining column metadata, and releasing the result set. -Use a row fetching method such as fetch_row or an iterator such as each to access the rows of the result set. -If you want a count of the number of rows in the result set: invoke 'num_rows' method. -Invoke 'free' to release the result set. - -== Example == - -require 'MonetDB' - -db = MonetDB.new -db.connect(user = "monetdb", passwd = "monetdb", lang = "sql", host="127.0.0.1", port = 50000, db_name = "demo", auth_type = "SHA1") - -# set type_cast=true to enable MonetDB to Ruby type mapping -res = db.query("SELECT * from tables;", type_cast = false) - -#puts res.debug_columns_type - -puts "Number of rows returned: " + res.num_rows.to_s -puts "Number of fields: " + res.num_fields.to_s - - -# Get the columns' name -col_names = res.name_fields - - -# Iterate over the record set and retrieve on row at a time -puts res.fetch -while row = res.fetch do - printf "%s \n", row -end - -# Release the result set. -res.free - -# Disconnect from server -db.close - -See lib/demo.rb and the MonetDBDatar class documentation for more examples. - - - -== ActiveRecord connector adapter == -Active Record connects business objects and database tables to create a persistable domain model where logic and data are presented in one wrapping. It‘s an implementation of the object-relational mapping (ORM) pattern. - -Required files: - -adapter/lib/active_record/monetdb_adapter.rb - -Usage example follows: -require 'active_record' - -ActiveRecord::Base.logger = Logger.new(STDERR) -ActiveRecord::Base.colorize_logging = true - -ActiveRecord::Base.establish_connection( - :adapter => "monetdb", - :host => "localhost", - :database => "demo" -) - -# Create a new table -class AddTests < ActiveRecord::Migration - def self.up - create_table :tests do |table| - table.column :name, :string - table.column :surname, :string - end - end - - def self.down - drop_table :tests - end - -end - -AddTests.up - -# Migration: add a column name with a default value -class AddAge < ActiveRecord::Migration - def self.up - add_column :tests, :age, :smallint, :default => 18 - end - - def self.down - remove_column :tests, :age - end - -end - -class Test < ActiveRecord::Base -end - -# Insert an entry in the table -Test.create(:name => 'X', :surname => 'Y') - -# add a column -AddAge.up - -# return the first result of the query SELECT * from tables -row = Test.find(:first) -printf "SELECT * from tests LIMIT 1:\n" -printf "Name: %s, Surname: %s, Age: %s\n", row.name, row.surname, row.age - -# Drop the table -AddTests.down - -== Rubygem == - -The standalone ruby driver can be distributed as a ruby gem. -A gem file is already available; however, it can be generated -starting from the ruby-monetdb-sql-0.1.gemspec file: - -$ gem build ruby-monetdb-sql-0.1.gemspec - -To install the file run the command: - -$ gem install ruby-monetdb-sql-0.1.gem - -Documentation in ri and html format will be generated and installed as well - diff --git a/TODO b/TODO deleted file mode 100644 index 8a4470bb1ccf98ed2a4bbfa241340a2e8aeac868..0000000000000000000000000000000000000000 --- a/TODO +++ /dev/null @@ -1,5 +0,0 @@ -* test and improve utf8 and type conversion - -* documentation cleanup - -* OSM on rails demo (slowed down due to third party plugins requirements) diff --git a/adapter/Makefile.ag b/adapter/Makefile.ag deleted file mode 100644 index b54b10b8363dcd124904aa54b1eecf3caed20665..0000000000000000000000000000000000000000 --- a/adapter/Makefile.ag +++ /dev/null @@ -1,24 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -gem_adapter = { - FILES = activerecord-monetdb-adapter-0.1.gemspec - DIR = $(prefix)/$(RUBY_DIR) -} - -EXTRA_DIST = activerecord-monetdb-adapter-0.1.gemspec -EXTRA_DIST_DIR = active_record lib diff --git a/adapter/activerecord-monetdb-adapter-0.1.gemspec b/adapter/activerecord-monetdb-adapter-0.1.gemspec deleted file mode 100644 index 83d91d3b6aba6d3961f8e63e5c6696a8e832c798..0000000000000000000000000000000000000000 --- a/adapter/activerecord-monetdb-adapter-0.1.gemspec +++ /dev/null @@ -1,18 +0,0 @@ -Gem::Specification.new do |s| - s.required_ruby_version = '>= 2.1.0' - s.name = %q{activerecord-monetdb-adapter} - s.version = "0.2" - s.date = %q{2009-05-18} - s.authors = ["G Modena"] - s.email = %q{gm@cwi.nl} - s.summary = %q{ActiveRecord Connector for MonetDB} - s.homepage = %q{http://monetdb.cwi.nl/} - s.description = %q{ActiveRecord Connector for MonetDB built on top of the pure Ruby database driver} - s.files = [ "lib/active_record/connection_adapters/monetdb_adapter.rb" ] - s.has_rdoc = true - s.require_path = 'lib' - s.add_dependency(%q<activerecord>, [">= 2.3.2"]) - s.add_dependency(%q<ruby-monetdb-sql>, [">= 0.1"]) - # placeholder project to avoid warning about not having a rubyforge_project - s.rubyforge_project = "nowarning" -end diff --git a/adapter/lib/active_record/connection_adapters/monetdb_adapter.rb b/adapter/lib/active_record/connection_adapters/monetdb_adapter.rb deleted file mode 100644 index fa5a7999f209c1dbe3a7f9fa7749ebb1ba411551..0000000000000000000000000000000000000000 --- a/adapter/lib/active_record/connection_adapters/monetdb_adapter.rb +++ /dev/null @@ -1,575 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -# MonetDB Active Record adapter -# monetdb_adapter.rb - -# The code is an adaption of the adapter developer by Michalis Polakis (2008), to work on top of the pure ruby MonetDB -# interface - -# Refreshed by Martin Samson (2011) - -MDB_SYS_SCHEMA = "sys." -MDB_NON_SYSTEM_TABLES_ONLY = "and system = false" - -require 'active_record/connection_adapters/abstract_adapter' -require 'MonetDB' - -module ActiveRecord - class Base - # Establishes a connection to the database that's used by all Active Record objects - def self.monetdb_connection(config) - # extract connection parameters - config = config.symbolize_keys - - host = config[:host] || "127.0.0.1" - port = config[:port] || 50000 - username = config[:username].to_s if config[:username] - password = config[:password].to_s if config[:password] - - # Use "sql" as default language if none is specified - lang = config[:lang] || "sql" - - if config.key?(:database) - database = config[:database] - else - raise ArgumentError, "No database specified. Missing argument: database." - end - - dbh = MonetDB.new - ConnectionAdapters::MonetDBAdapter.new(dbh, logger, [host, port, username, password, database, lang], config) - end - end - - - module ConnectionAdapters - class MonetDBColumn < Column - # Handles the case where column type is int but default - # column value is the next value of a sequence(string). - # By overriding this function, extract_default in - # schema_definitions does not return a fixnum(0 or 1) but - # the correct default value. - def type_cast(value) - if value.nil? - nil - elsif type == :integer && value =~/next value for/ - nil - else - super - end - end - - private - - def simplified_type(field_type) - case field_type - when /int|smallint/i - :integer - when /real|double/i - :float - when /datetime/i - :timestamp - when /timestamp/i - :timestamp - when /char/i, /varchar/i - :string - when /bigint/i - :bigint - else - super - end - end - - end #end of MonetDBColumn class - - class TableDefinition - # Override so that we handle the fact that MonetDB - # doesn't support "limit" on integer column. - # Otherwise same implementation - def column(name, type, options = {}) - column = self[name] || ColumnDefinition.new(@base, name, type) - - if type.to_sym != :integer and type.to_sym != :primary_key - column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym] - end - - column.precision = options[:precision] - column.scale = options[:scale] - column.default = options[:default] - column.null = options[:null] - - @columns << column unless @columns.include? column - self - end - - end - - class MonetDBAdapter < AbstractAdapter - class BindSubstitution < Arel::Visitors::MySQL - include Arel::Visitors::BindVisitor - end - - def initialize(connection, logger, connection_options, config) - super(connection, logger) - @visitor = BindSubstitution.new self - @connection_options, @config = connection_options, config - connect - end - - def adapter_name #:nodoc: - 'MonetDB' - end - - # Functions like rename_table, rename_column and - # change_column cannot be implemented in MonetDB. - def supports_migrations? - true - end - - # testing savepoints in progress - def supports_savepoints? #:nodoc: - true - end - - def support_transaction? #:nodoc: - false - end - - def supports_ddl_transactions? - false - end - - def native_database_types - { - :primary_key => "int NOT NULL auto_increment PRIMARY KEY", - :string => {:name => "varchar", :limit => 255}, - :text => {:name => "clob"}, - :integer => {:name => "int"}, - :float => {:name => "float"}, - :decimal => {:name => "decimal"}, - :datetime => {:name => "timestamp"}, - :timestamp => {:name => "timestamp"}, - :time => {:name => "time"}, - :date => {:name => "date"}, - :binary => {:name => "blob"}, - :boolean => {:name => "boolean"}, - :bigint => {:name => "bigint"} - } - end - - #MonetDB does not support using DISTINCT withing COUNT - #by default - def supports_count_distinct? - false - end - - #----------CONNECTION MANAGEMENT------------------ - - # Check if the connection is active - def active? - if @connection != nil - @connection.is_connected? - end - - return false - end - - # Close this connection and open a new one in its place. - def reconnect! - if @connection != nil - #@connection.reconnect - false - end - end - - def disconnect! - #@connection.auto_commit(flag=true) - @connection.close - end - - # -------END OF CONNECTION MANAGEMENT---------------- - - - # ===============SCHEMA DEFINITIONS===========# - - def binary_to_string(value) - res = "" - value.scan(/../).each { |i| res << i.hex.chr } - res - end - - # ===========END OF SCHEMA DEFINITIONS========# - - - #===============SCHEMA STATEMENTS===========# - # The following schema_statements.rb functions are not supported by MonetDB (19/5/2008). - # - # -rename_table : not such functionality by MonetDB's API. Altering some - # administratives' tables values has no result. - # - # -rename_column : Could be possible if I make a new_name column copy the - # data from the old column there and then drop that column. But I would - # also have to take care of references and other constraints. Not sure if - # this is desired. - # - # -change_column : Alteration of a column's datatype is not supported. - # NOTE WE MAY BE ABLE TO "CHANGE" A COLUMN DEFINITION IF WE DROP THE COLUMN - # AND CREATE A NEW ONE WITH THE OLD NAME. THIS COULD WORK AS LONG AS WE DON'T - # LOSE ANY DATA. - - - # Sets a new default value for a column. - # ===== Examples ===== - # change_column_default(:suppliers, :qualification, 'new') - # change_column_default(:accounts, :authorized, 1) - def change_column_default(table_name, column_name, default) - sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT" #{quote(default)}" - - if (default.nil? || (default.casecmp("NULL")==0)) - sql << " NULL" - else - sql << quote(default) - end - p "SQL: " + sql + '\n' - hdl = execute(sql) - end - - def remove_index(table_name, options = {}) - hdl = execute("DROP INDEX #{index_name(table_name, options)}") - end - - # MonetDB does not support limits on certain data types - # Limit is supported for the {char, varchar, clob, blob, time, timestamp} data types. - def type_to_sql(type, limit = nil, precision = nil, scale = nil) - return super if limit.nil? - - # strip off limits on data types not supporting them - if [:integer, :double, :date, :bigint].include? type - type.to_s - else - super - end - end - - # Returns an array of Column objects for the table specified by +table_name+. - def columns(table_name, name = nil) - return [] if table_name.to_s.strip.empty? - table_name = table_name.to_s if table_name.is_a?(Symbol) - table_name = table_name.split('.')[-1] unless table_name.nil? - - hdl = execute(" SELECT name, type, type_digits, type_scale, \"default\", \"null\" FROM #{MDB_SYS_SCHEMA}_columns WHERE table_id in (SELECT id FROM #{MDB_SYS_SCHEMA}_tables WHERE name = '#{table_name}' #{MDB_NON_SYSTEM_TABLES_ONLY})", name) - - num_rows = hdl.num_rows - return [] unless num_rows >= 1 - - result = [] - - - while row = hdl.fetch_hash do - col_name = row['name'] - col_default = row['default'] - # If there is no default value, it assigns NIL - col_default = nil if (col_default && col_default.upcase == 'NULL') - # Removes single quotes from the default value - col_default.gsub!(/^'(.*)'$/, '\1') unless col_default.nil? - - # A string is returned so we must convert it to boolean - col_nullable = row['null'] - - if (col_nullable.casecmp("true") == 0) - col_nullable = true - elsif (col_nullable.casecmp("false") == 0) - col_nullable = false - end - - col_type = row['type'] - type_digits = row['type_digits'] - type_scale = row['type_scale'] - - - # Don't care about datatypes that aren't supported by - # ActiveRecord, like interval. - # Also do nothing for datatypes that don't support limit - # like integer, double, date, bigint - if (col_type == "clob" || col_type == "blob") - if (type_digits.to_i > 0) - col_type << "(#{type_digits})" - end - elsif (col_type == "char" || - col_type == "varchar" || - col_type == "time" || - col_type == "timestamp" - ) - col_type << "(#{type_digits})" - elsif (col_type == "decimal") - if (type_scale.to_i == 0) - col_type << "(#{type_digits})" - else - col_type << "(#{type_digits},#{type_scale})" - end - end - - # instantiate a new column and insert into the result array - result << MonetDBColumn.new(col_name, col_default, col_type, col_nullable) - - end - - # check that free has been correctly performed - hdl.free - - return result - end - - def primary_key(table) - 'id' - end - - # Adds a new column to the named table. - # See TableDefinition#column for details of the options you can use. - def add_column(table_name, column_name, type, options = {}) - if ((type.to_sym == :decimal) && (options[:precision].to_i+options[:scale].to_i > 18)) - raise StandardError, "It is not possible to have a decimal column where Precision + Scale > 18 . The column will not be added to the table!" - return - else - super - end - end - - # Return an array with all non-system table names of the current - # database schema - def tables(name = nil) - cur_schema = select_value("select current_schema", name) - select_values(" SELECT t.name FROM #{MDB_SYS_SCHEMA}_tables t, sys.schemas s - WHERE s.name = '#{cur_schema}' - AND t.schema_id = s.id - AND t.system = false", name) - end - - # Returns an array of indexes for the given table. - def indexes(table_name, name = nil) - sql_query = " SELECT distinct i.name as index_name, k.\"name\", k.nr - FROM - #{MDB_SYS_SCHEMA}idxs i, #{MDB_SYS_SCHEMA}_tables t, #{MDB_SYS_SCHEMA}objects k - WHERE - i.type = 0 AND i.name not like '%pkey' - AND i.id = k.id AND t.id = i.table_id - AND t.name = '#{table_name.to_s}' - ORDER BY i.name, k.nr;" - result = select_all(sql_query, name); - - cur_index = nil - indexes = [] - - result.each do |row| - if cur_index != row['index_name'] - indexes << IndexDefinition.new(table_name, row['index_name'], false, []) - cur_index = row['index_name'] - end - - indexes.last.columns << row['name'] - end - - indexes - end - - # ===========END OF SCHEMA STATEMENTS========# - - # ===========QUOTING=========================# - def quote(value, column = nil) - if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary) - s = column.class.string_to_binary(value).unpack("H*")[0] - "BLOB '#{s}'" - else - super - end - end - - def quote_column_name(name) #:nodoc: - "\"#{name.to_s}\"" - end - - def quote_table_name(name) #:nodoc: - quote_column_name(name).gsub('.', '"."') - end - - # If the quoted true is 'true' MonetDB throws a string cast exception - def quoted_true - "true" - end - - # If the quoted false is 'false' MonetDB throws a string cast exception - def quoted_false - "false" - end - - # ===========END-OF-QUOTING==================# - - - # =========DATABASE=STATEMENTS===============# - - # Returns an array of arrays containing the field values. - # Order of columns in tuple arrays is not guaranteed. - def select_rows(sql, name = nil) - result = select(sql, name) - result.map { |v| v.values } - end - - def execute(sql, name = nil) - sql = sql.gsub('!=', '<>') - sql += ';' - @connection.query(sql) - end - - def exec_query(sql, name = nil, binds = []) - select_rows(sql, name) - end - - def last_inserted_id(result) - result.last_insert_id - end - - def delete(arel, name = nil, binds = []) - res = super(arel, name, binds) - res.affected_rows - end - - # Begins the transaction. - def begin_db_transaction - hdl = execute("START TRANSACTION") - end - - # Commits the transaction (ends TRANSACTIOM). - def commit_db_transaction - hdl = execute("COMMIT") - end - - # Rolls back the transaction. Must be - # done if the transaction block raises an exception or returns false (ends TRANSACTIOM). - def rollback_db_transaction - hdl = execute("ROLLBACK") - end - - def current_savepoint_name - @connection.transactions || 0 - end - - # Create a new savepoint - def create_savepoint - @connection.save - execute("SAVEPOINT #{current_savepoint_name}") - end - - # rollback to the last savepoint - def rollback_to_savepoint - execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}") - end - - # release current savepoint - def release_savepoint - execute("RELEASE SAVEPOINT #{current_savepoint_name}") - end - - def add_lock!(sql, options) - @logger.info "Warning: MonetDB :lock option '#{options[:lock].inspect}' not supported. Returning unmodified sql statement!" if @logger && options.has_key?(:lock) - sql - end - - def empty_insert_statement(table_name) - # Ensures that the auto-generated id value will not violate the primary key constraint. - # comment out for production code(?) - #make_sure_pk_works(table_name, nil) - #"INSERT INTO #{quote_table_name(table_name)}" - end - - #=======END=OF=DATABASE=STATEMENTS=========# - - # Returns an array of record hashes with the column names as keys and - # column values as values. - def select(sql, name = nil, binds = []) - hdl = execute(sql, name) - hdl.result_hashes - end - - # Executes the update statement and returns the number of rows affected. - def update_sql(sql, name = nil) - hdl = execute(sql, name) - hdl.affected_rows - end - - # Returns the last auto-generated ID from the affected table. - def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) - # Ensures that the auto-generated id value will not violate the - # primary key constraint. Read the comments of make_sure_pk_works - # and documentation for further information. - # comment out for production code(?) - # table_name = extract_table_name_from_insertion_query(sql) - # make_sure_pk_works(table_name,name) - hdl = execute(sql, name) - hdl.last_insert_id - end - - protected - # Some tests insert some tuples with the id values set. In other words, the sequence - # is not used to generate a value for the primary key column named id. When a new tuple - # it to be inserted, where the id value is not set explicitly, a primary key violation will - # be raised because the generated from the sequence value is the same as one of the existing - # id values. This happens in unit tests quite often. So this function serves that the unit tests - # pass. However it is very expensive( sends 4 queries to the server) and probably not suitable for - # production code. Check the implementation for further info/details. - def make_sure_pk_works(table_name, name) - # Ensure the auto-generated id will not violate the primary key constraint. - # This is expensive and it's used so that the tests pass. Comment out for production code(?). - # Assume that table name has one primary key column named id that is associated with a sequence, - # otherwise return - hdl = nil - sequence_name = extract_sequence_name(select_value("select \"default\" from #{MDB_SYS_SCHEMA}_columns where table_id in (select id from #{MDB_SYS_SCHEMA}_tables where name = '#{table_name}') and name='id';")) - - return if sequence_name.blank? - - max_id = select_value("select max(id) from #{table_name}").to_i - next_seq_val = select_value("select next value for #{sequence_name}").to_i - - if (max_id > next_seq_val) - hdl = execute("ALTER SEQUENCE #{sequence_name} RESTART WITH #{max_id+1}", name) - else - hdl = execute("ALTER SEQUENCE #{sequence_name} RESTART WITH #{next_seq_val+1}", name) - end - end - - # Auxiliary function that extracts the table name from an insertion query - # It's called by insert_sql in order to assist at make_sure_pk_works. - # Ideally, if make_sure_pk_works is commented out for production code, this - # function will be never called. - def extract_table_name_from_insertion_query(sql) - $1 if sql =~ /INSERT INTO "(.*)" \(/ - end - - # Auxiliary function that extracts the sequence name. - # It's called by make_sure_pk_works. - # Ideally, if make_sure_pk_works is commented out for production code, this - # function will be never called. - def extract_sequence_name(seqStr) - $1 if seqStr =~ /\."(.*)"/ - end - - private - def connect - @connection.connect(user = @connection_options[2], passwd = @connection_options[3], lang = @connection_options[5], host = @connection_options[0], port = @connection_options[1], db_name = @connection_options[4], auth_type = "SHA1") if @connection - end - end - end -end diff --git a/lib/MonetDB.rb b/lib/MonetDB.rb deleted file mode 100644 index d1936dc464ea7f74f7d87c025be17a2fa117d61f..0000000000000000000000000000000000000000 --- a/lib/MonetDB.rb +++ /dev/null @@ -1,312 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - - -# A typical sequence of events is as follows: -# Fire a query using the database handle to send the statement to the server and get back a result set object. - -# A result set object has methods for fetching rows, moving around in the result set, obtaining column metadata, and releasing the result set. -# Use a row fetching method such as 'fetch_row' or an iterator such as each to access the rows of the result set. -# Call 'free' to release the result set. - - -#module MonetDB - - require 'MonetDBConnection' - require 'MonetDBData' - require 'MonetDBExceptions' - - # = Introduction - # - # A typical sequence of events is as follows: - # Create a database instance (handle), invoke query using the database handle to send the statement to the server and get back a result set object. - # - # A result set object has methods for fetching rows, moving around in the result set, obtaining column metadata, and releasing the result set. - # A result set object is an instance of the MonetDBData class. - # - # Records can be returneds as arrays and iterators over the set. - # - # A database handler (dbh) is and instance of the MonetDB class. - # - # = Connection management - # - # connect - establish a new connection - # * user: username (default is monetdb) - # * passwd: password (default is monetdb) - # * lang: language (default is sql) - # * host: server hostanme or ip (default is localhost) - # * port: server port (default is 50000) - # * db_name: name of the database to connect to - # * auth_type: hashing function to use during authentication (default is SHA1) - # - # is_connected? - returns true if there is an active connection to a server, false otherwise - # reconnect - recconnect to a server - # close - terminate a connection - # auto_commit? - returns ture if the session is running in auto commit mode, false otherwise - # auto_commit - enable/disable auto commit mode. - # - # query - fire a query - # - # Currently MAPI protocols 8 and 9 are supported. - # - # = Managing record sets - # - # - # A record set is represented as an instance of the MonetDBData class; the class provides methods to manage retrieved data. - # - # - # The following methods allow to iterate over data: - # - # fetch - iterates over the record set and retrieves on row at a time. Each row is returned as an array. - # fetch_hash - iterates over columns (on cell at a time). - # fetch_all_hash - returns record set entries hashed by column name orderd by column position. - # - # To return the record set as an array (with each tuple stored as array of fields) the following method can be used: - # - # fetch_all - fetch all rows and store them - # - # - # Information about the retrieved record set can be obtained via the following methods: - # - # num_rows - returns the number of rows present in the record set - # num_fields - returns the number of fields (columns) that compose the schema - # name_fields - returns the (ordered) name of the schema's columns - # type_fields - returns the (ordered) types list of the schema's columns - # - # To release a record set MonetDBData#free can be used. - # - # = Type conversion - # - # A mapping between SQL and ruby type is supported. Each retrieved field can be converted to a ruby datatype via - # a getTYPE method. - # - # The currently supported cast methods are: - # - # getInt - convert to an integer value - # getFloat - convert to a floating point value - # getString - return a string representation of the value, with trailing and leading " characters removed - # getBlob - convert an SQL stored HEX string to its binary representation - # getTime - return a string representation of a TIME field - # getDate - return a string representation of a DATE field - # getDateTime - convert a TIMESTAMP field to a ruby Time object - # getChar - on ruby >= 1.9, convert a CHAR field to char - # getBool - convert a BOOLEAN field to a ruby bool object. If the value of the field is unknown, nil is returned - # getNull - convert a NULL value to a nil object - # - # - # = Transactions - # - # By default monetdb works in auto_commit mode. To turn this feature off MonetDB#auto_commit(flag=false) can be used. - # - # Once auto_commit has been disable it is possible to start transactions, create/delete savepoints, rollback and commit with - # the usual SQL statements. - # - # Savepoints IDs can be generated using the MonetDB#save method. To release a savepoint ID use MonetDB#release. - # - # Savepoints can be accessed (as a stack) with the MonetDB#transactions method. - # - # example/standalone.rb contains usage example of the above mentioned methods. - - # = Introduction - # - # A typical sequence of events is as follows: - # Create a database instance (handle), invoke query using the database handle to send the statement to the server and get back a result set object. - # - # A result set object has methods for fetching rows, moving around in the result set, obtaining column metadata, and releasing the result set. - # A result set object is an instance of the MonetDBData class. - # - # Records can be returneds as arrays and iterators over the set. - # - # A database handler (dbh) is and instance of the MonetDB class. - # - # = Connection management - # - # connect - establish a new connection - # * user: username (default is monetdb) - # * passwd: password (default is monetdb) - # * lang: language (default is sql) - # * host: server hostanme or ip (default is localhost) - # * port: server port (default is 50000) - # * db_name: name of the database to connect to - # * auth_type: hashing function to use during authentication (default is SHA1) - # - # is_connected? - returns true if there is an active connection to a server, false otherwise - # reconnect - recconnect to a server - # close - terminate a connection - # auto_commit? - returns ture if the session is running in auto commit mode, false otherwise - # auto_commit - enable/disable auto commit mode. - # - # query - fire a query - # - # Currently MAPI protocols 8 and 9 are supported. - # - # = Managing record sets - # - # - # A record set is represented as an instance of the MonetDBData class; the class provides methods to manage retrieved data. - # - # - # The following methods allow to iterate over data: - # - # fetch - iterates over the record set and retrieves on row at a time. Each row is returned as an array. - # fetch_hash - iterates over columns (on cell at a time). - # fetch_all_hash - returns record set entries hashed by column name orderd by column position. - # - # To return the record set as an array (with each tuple stored as array of fields) the following method can be used: - # - # fetch_all - fetch all rows and store them - # - # - # Information about the retrieved record set can be obtained via the following methods: - # - # num_rows - returns the number of rows present in the record set - # num_fields - returns the number of fields (columns) that compose the schema - # name_fields - returns the (ordered) name of the schema's columns - # - # To release a record set MonetDBData#free can be used. - # - # = Type conversion - # - # Invoking MonetDB#query with the flag type_conversion=true will result in a type cast of the record set fields from SQL types to ruby types - # - # demo.rb contains usage example of the above mentioned methods. - - class MonetDB - DEFAULT_USERNAME = "monetdb" - DEFAULT_PASSWORD = "monetdb" - DEFAULT_LANG = LANG_SQL - DEFAULT_HOST = "127.0.0.1" - DEFAULT_PORT = 50000 - DEFAULT_DATABASE = "test" - DEFAULT_AUTHTYPE = "SHA1" - - def initalize() - @connection = nil - end - - # Establish a new connection. - # * username: username (default is monetdb) - # * password: password (default is monetdb) - # * lang: language (default is sql) - # * host: server hostanme or ip (default is localhost) - # * port: server port (default is 50000) - # * db_name: name of the database to connect to - # * auth_type: hashing function to use during authentication (default is SHA1) - def connect(username=DEFAULT_USERNAME, password=DEFAULT_PASSWORD, lang=DEFAULT_LANG, host=DEFAULT_HOST, port=DEFAULT_PORT, db_name=DEFAULT_DATABASE, auth_type=DEFAULT_AUTHTYPE) - # TODO: handle pools of connections - - @username = username - @password = password - @lang = lang - @host = host - @port = port - @db_name = db_name - @auth_type = auth_type - - @connection = MonetDBConnection.new(user = @username, passwd = @password, lang = @lang, host = @host, port = @port) - @connection.connect(@db_name, @auth_type) - end - - # Establish a new connection using named parameters. - # * user: username (default is monetdb) - # * passwd: password (default is monetdb) - # * language: lang (default is sql) - # * host: host to connect to (default is localhost) - # * port: port to connect to (default is 50000) - # * database: name of the database to connect to - # * auth_type: hashing function to use during authentication (default is SHA1) - # - # Conventionally named parameters are passed as an hash. - # - # Ruby 1.8: - # MonetDB::conn({ :user => "username", :passwd => "password", :database => "database"}) - # - # Ruby 1.9: - # MonetDB::conn(user: "username", passwd: "password", database: "database") - def conn(options) - user = options[:user] || DEFAULT_USERNAME - passwd = options[:passwd] || DEFAULT_PASSWORD - language = options[:language] || DEFAULT_LANG - host = options[:host] || DEFAULT_HOST - port = options[:port] || DEFAULT_PORT - database = options[:database] || DEFAULT_DATABASE - auth_type = options[:auth_type] || DEFAULT_AUTHTYPE - - connect(user, passwd, language, host, port, database, auth_type) - end - - # Send a <b> user submitted </b> query to the server and store the response. - # Returns and instance of MonetDBData. - def query(q="") - if @connection != nil - @data = MonetDBData.new(@connection) - @data.execute(q) - end - return @data - end - - # Return true if there exists a "connection" object - def is_connected? - if @connection == nil - return false - else - return true - end - end - - # Reconnect to the server - def reconnect - if @connection != nil - self.close - - @connection = MonetDBConnection.new(user = @username, passwd = @password, lang = @lang, host = @host, port = @port) - @connection.connect(db_name = @db_name, auth_type = @auth_type) - end - end - - # Turn auto commit on/off - def auto_commit(flag=true) - @connection.set_auto_commit(flag) - end - - # Returns the current auto commit (on/off) settings. - def auto_commit? - @connection.auto_commit? - end - - # Returns the name of the last savepoint in a transactions pool - def transactions - @connection.savepoint - end - - # Create a new savepoint ID - def save - @connection.transactions.save - end - - # Release a savepoint ID - def release - @connection.transactions.release - end - - # Close an active connection - def close() - @connection.disconnect - @connection = nil - end - end -#end diff --git a/lib/MonetDBConnection.rb b/lib/MonetDBConnection.rb deleted file mode 100644 index e18df90bc14416eb5651a3457488dc53d2cd007e..0000000000000000000000000000000000000000 --- a/lib/MonetDBConnection.rb +++ /dev/null @@ -1,548 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -# Implements the MAPI communication protocol - - -require 'socket' -require 'time' -require 'hasher' -require 'MonetDBExceptions' -require 'uri' # parse merovingian redirects - -Q_TABLE = "1" # SELECT operation -Q_UPDATE = "2" # INSERT/UPDATE operations -Q_CREATE = "3" # CREATE/DROP TABLE operations -Q_TRANSACTION = "4" # TRANSACTION -Q_PREPARE = "5" -Q_BLOCK = "6" # QBLOCK message - -MSG_REDIRECT = '^' # auth redirection through merovingian -MSG_QUERY = '&' -MSG_SCHEMA_HEADER = '%' -MSG_INFO = '!' # info response from mserver -MSG_TUPLE = '[' -MSG_PROMPT = "" - - -REPLY_SIZE = '-1' - -MAX_AUTH_ITERATION = 10 # maximum number of atuh iterations (thorough merovingian) allowed - -MONET_ERROR = -1 - -LANG_SQL = "sql" - -# Protocols -MAPIv8 = 8 -MAPIv9 = 9 - -MONETDB_MEROVINGIAN = "merovingian" -MONETDB_MSERVER = "monetdb" - -MEROVINGIAN_MAX_ITERATIONS = 10 - -class MonetDBConnection - # enable debug output - @@DEBUG = false - - # hour in seconds, used for timezone calculation - @@HOUR = 3600 - - # maximum size (in bytes) for a monetdb message to be sent - @@MAX_MESSAGE_SIZE = 2048 - - # endianness of a message sent to the server - @@CLIENT_ENDIANNESS = "BIG" - - # MAPI protocols supported by the driver - @@SUPPORTED_PROTOCOLS = [MAPIv8, MAPIv9] - - attr_reader :socket, :auto_commit, :transactions, :lang - - # Instantiates a new MonetDBConnection object - # * user: username (default is monetdb) - # * passwd: password (default is monetdb) - # * lang: language (default is sql) - # * host: server hostanme or ip (default is localhost) - # * port: server port (default is 50000) - - def initialize(user = "monetdb", passwd = "monetdb", lang = "sql", host="127.0.0.1", port = "50000") - @user = user - @passwd = passwd - @lang = lang.downcase - @host = host - @port = port - - @client_endianness = @@CLIENT_ENDIANNESS - - @auth_iteration = 0 - @connection_established = false - - @transactions = MonetDBTransaction.new # handles a pool of transactions (generates and keeps track of savepoints) - - if @@DEBUG == true - require 'logger' - end - - if @lang[0, 3] == 'sql' - @lang = "sql" - end - - end - - # Connect to the database, creates a new socket - def connect(db_name = 'demo', auth_type = 'SHA1') - @database = db_name - @auth_type = auth_type - - @socket = TCPSocket.new(@host, @port.to_i) - if real_connect - if @lang == LANG_SQL - set_timezone - set_reply_size - end - true - end - false - end - - # perform a real connection; retrieve challenge, proxy through merovinginan, build challenge and set the timezone - def real_connect - - server_challenge = retrieve_server_challenge() - if server_challenge != nil - salt = server_challenge.split(':')[0] - @server_name = server_challenge.split(':')[1] - @protocol = server_challenge.split(':')[2].to_i - @supported_auth_types = server_challenge.split(':')[3].split(',') - @server_endianness = server_challenge.split(':')[4] - if @protocol == MAPIv9 - @pwhash = server_challenge.split(':')[5] - end - else - raise MonetDBConnectionError, "Error: server returned an empty challenge string." - end - - # The server supports only RIPMED168 or crypt as an authentication hash function, but the driver does not. - if @supported_auth_types.length == 1 - auth = @supported_auth_types[0] - if auth.upcase == "RIPEMD160" or auth.upcase == "CRYPT" - raise MonetDBConnectionError, auth.upcase + " " + ": algorithm not supported by ruby-monetdb." - end - end - - - # If the server protocol version is not 8: abort and notify the user. - if @@SUPPORTED_PROTOCOLS.include?(@protocol) == false - raise MonetDBProtocolError, "Protocol not supported. The current implementation of ruby-monetdb works with MAPI protocols #{@@SUPPORTED_PROTOCOLS} only." - - elsif mapi_proto_v8? - reply = build_auth_string_v8(@auth_type, salt, @database) - elsif mapi_proto_v9? - reply = build_auth_string_v9(@auth_type, salt, @database) - end - - if @socket != nil - @connection_established = true - - send(reply) - monetdb_auth = receive - - if monetdb_auth.length == 0 - # auth succedeed - true - else - if monetdb_auth[0].chr == MSG_REDIRECT - #redirection - - redirects = [] # store a list of possible redirects - - monetdb_auth.split('\n').each do |m| - # strip the trailing ^mapi: - # if the redirect string start with something != "^mapi:" or is empty, the redirect is invalid and shall not be included. - if m[0..5] == "^mapi:" - redir = m[6..m.length] - # url parse redir - redirects.push(redir) - else - $stderr.print "Warning: Invalid Redirect #{m}" - end - end - - if redirects.size == 0 - raise MonetDBConnectionError, "No valid redirect received" - else - begin - uri = URI.split(redirects[0]) - # Splits the string on following parts and returns array with result: - # - # * Scheme - # * Userinfo - # * Host - # * Port - # * Registry - # * Path - # * Opaque - # * Query - # * Fragment - server_name = uri[0] - host = uri[2] - port = uri[3] - database = uri[5].gsub(/^\//, '') if uri[5] != nil - rescue URI::InvalidURIError - raise MonetDBConnectionError, "Invalid redirect: #{redirects[0]}" - end - end - - if server_name == MONETDB_MEROVINGIAN - if @auth_iteration <= MEROVINGIAN_MAX_ITERATIONS - @auth_iteration += 1 - real_connect - else - raise MonetDBConnectionError, "Merovingian: too many iterations while proxying." - end - elsif server_name == MONETDB_MSERVER - begin - @socket.close - rescue - raise MonetDBConnectionError, "I/O error while closing connection to #{@socket}" - end - # reinitialize a connection - @host = host - @port = port - - connect(database, @auth_type) - else - @connection_established = false - raise MonetDBConnectionError, monetdb_auth - end - elsif monetdb_auth[0].chr == MSG_INFO - raise MonetDBConnectionError, monetdb_auth - end - end - end - end - - def savepoint - @transactions.savepoint - end - - # Formats a <i>command</i> string so that it can be parsed by the server - def format_command(x) - return "X" + x + "\n" - end - - # send an 'export' command to the server - def set_export(id, idx, offset) - send(format_command("export " + id.to_s + " " + idx.to_s + " " + offset.to_s)) - end - - # send a 'reply_size' command to the server - def set_reply_size - send(format_command(("reply_size " + REPLY_SIZE))) - - response = receive - - if response == MSG_PROMPT - true - elsif response[0] == MSG_INFO - raise MonetDBCommandError, "Unable to set reply_size: #{response}" - end - - end - - def set_output_seq - send(format_command("output seq")) - end - - # Disconnect from server - def disconnect() - if @connection_established - begin - @socket.close - rescue => e - $stderr.print e - end - else - raise MonetDBConnectionError, "No connection established." - end - end - - # send data to a monetdb5 server instance and returns server's response - def send(data) - encode_message(data).each do |m| - @socket.write(m) - end - end - - # receive data from a monetdb5 server instance - def receive - is_final, chunk_size = recv_decode_hdr - - if chunk_size == 0 - return "" # needed on ruby-1.8.6 linux/64bit; recv(0) hangs on this configuration. - end - - data = @socket.recv(chunk_size) - - if is_final == false - while is_final == false - is_final, chunk_size = recv_decode_hdr - data += @socket.recv(chunk_size) - end - end - - return data - end - - # Builds and authentication string given the parameters submitted by the user (MAPI protocol v8). - # - def build_auth_string_v8(auth_type, salt, db_name) - # seed = password + salt - if (auth_type.upcase == "MD5" or auth_type.upcase == "SHA1") and @supported_auth_types.include?(auth_type.upcase) - auth_type = auth_type.upcase - digest = Hasher.new(auth_type, @passwd+salt) - hashsum = digest.hashsum - elsif auth_type.downcase == "plain" or not @supported_auth_types.include?(auth_type.upcase) - auth_type = 'plain' - hashsum = @passwd + salt - - elsif auth_type.downcase == "crypt" - auth_type = @supported_auth_types[@supported_auth_types.index(auth_type)+1] - $stderr.print "The selected hashing algorithm is not supported by the Ruby driver. #{auth_type} will be used instead." - digest = Hasher.new(auth_type, @passwd+salt) - hashsum = digest.hashsum - else - # The user selected an auth type not supported by the server. - raise MonetDBConnectionError, "#{auth_type} not supported by the server. Please choose one from #{@supported_auth_types}" - - end - # Build the reply message with header - reply = @client_endianness + ":" + @user + ":{" + auth_type + "}" + hashsum + ":" + @lang + ":" + db_name + ":" - end - - # - # Builds and authentication string given the parameters submitted by the user (MAPI protocol v9). - # - def build_auth_string_v9(auth_type, salt, db_name) - if (auth_type.upcase == "MD5" or auth_type.upcase == "SHA1") and @supported_auth_types.include?(auth_type.upcase) - auth_type = auth_type.upcase - # Hash the password - pwhash = Hasher.new(@pwhash, @passwd) - - digest = Hasher.new(auth_type, pwhash.hashsum + salt) - hashsum = digest.hashsum - - elsif auth_type.downcase == "plain" # or not @supported_auth_types.include?(auth_type.upcase) - # Keep it for compatibility with merovingian - auth_type = 'plain' - hashsum = @passwd + salt - elsif @supported_auth_types.include?(auth_type.upcase) - if auth_type.upcase == "RIPEMD160" - auth_type = @supported_auth_types[@supported_auth_types.index(auth_type)+1] - $stderr.print "The selected hashing algorithm is not supported by the Ruby driver. #{auth_type} will be used instead." - end - # Hash the password - pwhash = Hasher.new(@pwhash, @passwd) - - digest = Hasher.new(auth_type, pwhash.hashsum + salt) - hashsum = digest.hashsum - else - # The user selected an auth type not supported by the server. - raise MonetDBConnectionError, "#{auth_type} not supported by the server. Please choose one from #{@supported_auth_types}" - end - # Build the reply message with header - reply = @client_endianness + ":" + @user + ":{" + auth_type + "}" + hashsum + ":" + @lang + ":" + db_name + ":" - end - - # builds a message to be sent to the server - def encode_message(msg = "") - message = Array.new - data = "" - - hdr = 0 # package header - pos = 0 - is_final = false # last package in the stream - - while (!is_final) - data = msg[pos..pos+[@@MAX_MESSAGE_SIZE.to_i, (msg.length - pos).to_i].min] - pos += data.length - - if (msg.length - pos) == 0 - last_bit = 1 - is_final = true - else - last_bit = 0 - end - - hdr = [(data.length << 1) | last_bit].pack('v') - - message << hdr + data.to_s # Short Little Endian Encoding - end - - message.freeze # freeze and return the encode message - end - - # Used as the first step in the authentication phase; retrives a challenge string from the server. - def retrieve_server_challenge() - server_challenge = receive - end - - # reads and decodes the header of a server message - def recv_decode_hdr() - if @socket != nil - fb = @socket.recv(1) - sb = @socket.recv(1) - - # Use execeptions handling to keep compatibility between different ruby - # versions. - # - # Chars are treated differently in ruby 1.8 and 1.9 - # try do to ascii to int conversion using ord (ruby 1.9) - # and if it fail fallback to character.to_i (ruby 1.8) - begin - fb = fb[0].ord - sb = sb[0].ord - rescue NoMethodError => one_eight - fb = fb[0].to_i - sb = sb[0].to_i - end - - chunk_size = (sb << 7) | (fb >> 1) - - is_final = false - if ((fb & 1) == 1) - is_final = true - - end - # return the size of the chunk (in bytes) - return is_final, chunk_size - else - raise MonetDBSocketError, "Error while receiving data\n" - end - end - - # Sets the time zone according to the Operating System settings - def set_timezone() - tz = Time.new - tz_offset = "%+03d:00" % (tz.gmt_offset / @@HOUR) - - query_tz = "sSET TIME ZONE INTERVAL '#{tz_offset}' HOUR TO MINUTE;" - - # Perform the query directly within the method - send(query_tz) - response = receive() - - if response == MSG_PROMPT - true - elsif response[0].chr == MSG_INFO - raise MonetDBQueryError, response - end - end - - # Turns auto commit on/off - def set_auto_commit(flag=true) - if flag == false - ac = " 0" - else - ac = " 1" - end - - send(format_command("auto_commit " + ac)) - - response = receive - if response == MSG_PROMPT - @auto_commit = flag - elsif response[0].chr == MSG_INFO - raise MonetDBCommandError, response - return - end - - end - - # Check the auto commit status (on/off) - def auto_commit? - @auto_commit - end - - # Check if monetdb is running behind the merovingian proxy and forward the connection in case - def merovingian? - if @server_name.downcase == MONETDB_MEROVINGIAN - true - else - false - end - end - - def mserver? - if @server_name.downcase == MONETDB_MSERVER - true - else - false - end - end - - # Check which protocol is spoken by the server - def mapi_proto_v8? - if @protocol == MAPIv8 - true - else - false - end - end - - def mapi_proto_v9? - if @protocol == MAPIv9 - true - else - false - end - end -end - -# handles transactions and savepoints. Can be used to simulate nested transactions. -class MonetDBTransaction - SAVEPOINT_STRING = "monetdbsp" - - def initialize - @id = 0 - @savepoint = "" - end - - def savepoint - @savepoint = SAVEPOINT_STRING + @id.to_s - end - - def release - prev_id - end - - def save - next_id - end - - private - def next_id - @id += 1 - end - - def prev_id - @id -= 1 - end - -end diff --git a/lib/MonetDBData.rb b/lib/MonetDBData.rb deleted file mode 100644 index 7dd8509aca9653976a73c9350167cbf17e6b054c..0000000000000000000000000000000000000000 --- a/lib/MonetDBData.rb +++ /dev/null @@ -1,420 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -# Models a MonetDB RecordSet -require 'time' -require 'ostruct' - -require "bigdecimal" - -require 'MonetDBConnection' - -require 'logger' - -class MonetDBData - @@DEBUG = false - attr_accessor :last_insert_id, :affected_rows - - def initialize(connection) - @connection = connection - @lang = @connection.lang - - # Structure containing the header+results set for a fired Q_TABLE query - @header = [] - @query = {} - - @record_set = [] - @index = 0 # Position of the last returned record - - @row_count = 0 - @row_offset = 10 - @row_index = Integer(REPLY_SIZE) - end - - # Fire a query and return the server response - def execute(q) - # fire a query and get ready to receive the data - @connection.send(format_query(q)) - data = @connection.receive - - return if data == nil - - # temporarly store retrieved rows - record_set = receive_record_set(data) - - if (@lang == LANG_SQL) - # the fired query is a SELECT; store and return the whole record set - if @action == Q_TABLE - @header = parse_header_table(@header) - @header.freeze - - if @row_index.to_i < @row_count.to_i - block_rows = "" - while next_block - data = @connection.receive - block_rows += receive_record_set(data) - end - record_set += block_rows - end - end - - # ruby string management seems to not properly understand the MSG_PROMPT escape character. - # In order to avoid data loss the @record_set array is built once that all tuples have been retrieved - @record_set = record_set.split("\t]\n") - - if @record_set.length != @query['rows'].to_i - raise MonetDBQueryError, "Warning: Query #{@query['id']} declared to result in #{@query['rows']} but #{@record_set.length} returned instead" - end - end - @record_set.freeze - end - - # Free memory used to store the record set - def free() - @connection = nil - - @header = [] - @query = {} - - @record_set = [] - @index = 0 # Position of the last returned record - - - @row_index = Integer(REPLY_SIZE) - @row_count = 0 - @row_offset = 10 - - end - - # Returns the record set entries hashed by column name orderd by column position - def fetch_all_hash() - columns = {} - @header["columns_name"].each do |col_name| - columns[col_name] = fetch_column_name(col_name) - end - - return columns - end - - def fetch_hash() - if @index >= @query['rows'].to_i - return false - else - columns = {} - @header["columns_name"].each do |col_name| - position = @header["columns_order"].fetch(col_name) - row = parse_tuple(@record_set[@index]) - columns[col_name] = row[position] - end - @index += 1 - return columns - end - end - - # Returns the values for the column 'field' - def fetch_column_name(field="") - position = @header["columns_order"].fetch(field) - - col = Array.new - # Scan the record set by row - @record_set.each do |row| - col << parse_tuple(row)[position] - end - - return col - end - - # returns result as an array of hashes - def result_hashes - result = [] - @record_set.each do |row| - rec = parse_tuple(row) - hash = {} - @header['columns_name'].each_with_index do |item, i| - hash[item] = rec[i] - hash[item] = nil if hash[item] == 'NULL' - end - result << hash - end - result - end - - def fetch() - @index - if @index > @query['rows'].to_i - false - else - parse_tuple(@record_set[@index]) - @index += 1 - end - end - - # Cursor method that retrieves all the records present in a table and stores them in a cache. - def fetch_all() - if @query['type'] == Q_TABLE - rows = Array.new - @record_set.each do |row| - rows << parse_tuple(row) - end - @index = Integer(rows.length) - else - raise MonetDBDataError, "There is no record set currently available" - end - - return rows - end - - # Returns the number of rows in the record set - def num_rows() - return @query['rows'].to_i - end - - # Returns the number of fields in the record set - def num_fields() - return @query['columns'].to_i - end - - # Returns the (ordered) name of the columns in the record set - def name_fields() - return @header['columns_name'] - end - - # Returns the (ordered) name of the columns in the record set - def type_fields - return @header['columns_type'] - end - - private - - # store block of data, parse it and store it. - def receive_record_set(response) - rows = "" - lines = response.lines.to_a - response.each_line do |row| - if row[0].chr == MSG_QUERY - if row[1].chr == Q_TABLE - @action = Q_TABLE - @query = parse_header_query(row) - @query.freeze - @row_count = @query['rows'].to_i #total number of rows in table - elsif row[1].chr == Q_BLOCK - # strip the block header from data - @action = Q_BLOCK - @block = parse_header_query(row) - elsif row[1].chr == Q_TRANSACTION - @action = Q_TRANSACTION - elsif row[1].chr == Q_CREATE - @action = Q_CREATE - elsif row[1].chr == Q_UPDATE - @action = Q_UPDATE - result = row.split(' ') - @affected_rows = result[1].to_i - @last_insert_id = result[2].to_i - end - elsif row[0].chr == MSG_INFO - raise MonetDBQueryError, row - elsif row[0].chr == MSG_SCHEMA_HEADER - # process header data - @header << row - elsif row[0].chr == MSG_TUPLE - if REPLY_SIZE.to_i == -1 - # if all results are returned in this response, we don't have to look ahead further - return lines.join - end - rows += row - elsif row[0] == MSG_PROMPT - return rows - end - lines.shift - end - rows # return an array of unparsed tuples - end - - def next_block - if REPLY_SIZE.to_i == -1 or @row_index == @row_count - return false - else - # The increment step is small to better deal with ruby socket's performance. - # For larger values of the step performance drop; - # - @row_offset = [@row_offset, (@row_count - @row_index)].min - - # export offset amount - @connection.set_export(@query['id'], @row_index.to_s, @row_offset.to_s) - @row_index += @row_offset - @row_offset += 1 - end - return true - end - - # Formats a query <i>string</i> so that it can be parsed by the server - def format_query(q) - if @lang == LANG_SQL - return "s" + q + ";" - else - raise LanguageNotSupported, @lang - end - end - - # parse one tuple as returned from the server - def parse_tuple(tuple) - fields = Array.new - # remove trailing "[" - tuple = tuple.to_s.gsub(/^\[\s+/, '') - - tuple.split(/,\t/).each do |f| - fields << f.gsub(/\\n/, "\n").gsub(/\\/, '').gsub(/^"/, '').gsub(/"$/, '').gsub(/\"/, '') - end - - return fields.freeze - end - - # Parses a query header and returns information about the query. - def parse_header_query(row) - type = row[1].chr - if type == Q_TABLE - # Performing a SELECT: store informations about the table size, query id, total number of records and returned. - id = row.split(' ')[1] - rows = row.split(' ')[2] - columns = row.split(' ')[3] - returned = row.split(' ')[4] - - header = {"id" => id, "type" => type, "rows" => rows, "columns" => columns, "returned" => returned} - elsif type == Q_BLOCK - # processing block header - - id = row.split(' ')[1] - columns = row.split(' ')[2] - remains = row.split(' ')[3] - offset = row.split(' ')[4] - - header = {"id" => id, "type" => type, "remains" => remains, "columns" => columns, "offset" => offset} - else - header = {"type" => type} - end - - return header.freeze - end - - # Parses a Q_TABLE header and returns information about the schema. - def parse_header_table(header_t) - if @query["type"] == Q_TABLE - if header_t != nil - name_t = header_t[0].split(' ')[1].gsub(/,$/, '') - name_cols = Array.new - - header_t[1].split('%')[1].gsub(/'^\%'/, '').split('#')[0].split(' ').each do |col| - name_cols << col.gsub(/,$/, '') - end - - type_cols = {} - header_t[2].split('%')[1].gsub(/'^\%'/, '').split('#')[0].split(' ').each_with_index do |col, i| - if col.gsub(/,$/, '') != nil - type_cols[name_cols[i]] = col.gsub(/,$/, '') - end - end - - length_cols = {} - header_t[3].split('%')[1].gsub(/'^\%'/, '').split('#')[0].split(' ').each_with_index do |col, i| - length_cols[name_cols[i]] = col.gsub(/,$/, '') - end - - columns_order = {} - name_cols.each_with_index do |col, i| - columns_order[col] = i - end - - return {"table_name" => name_t, "columns_name" => name_cols, "columns_type" => type_cols, - "columns_length" => length_cols, "columns_order" => columns_order}.freeze - end - end - end -end - -# Overload the class string to convert monetdb to ruby types. -class String - def getInt - self.to_i - end - - def getFloat - self.to_f - end - - def getString - self.gsub(/^"/, '').gsub(/"$/, '') - end - - def getBlob - # first strip trailing and leading " characters - self.gsub(/^"/, '').gsub(/"$/, '') - - # convert from HEX to the origianl binary data. - blob = "" - self.scan(/../) { |tuple| blob += tuple.hex.chr } - return blob - end - - # ruby currently supports only time + date frommatted timestamps; - # treat TIME and DATE as strings. - def getTime - # HH:MM:SS - self.gsub(/^"/, '').gsub(/"$/, '') - end - - def getDate - self.gsub(/^"/, '').gsub(/"$/, '') - end - - def getDateTime - #YYYY-MM-DD HH:MM:SS - date = self.split(' ')[0].split('-') - time = self.split(' ')[1].split(':') - - Time.gm(date[0], date[1], date[2], time[0], time[1], time[2]) - end - - def getChar - # ruby < 1.9 does not have a Char datatype - begin - c = self.ord - rescue - c = self - end - - return c - end - - def getBool - if ['1', 'y', 't', 'true'].include?(self) - return true - elsif ['0', 'n', 'f', 'false'].include?(self) - return false - else - # unknown - return nil - end - end - - def getNull - if self.upcase == 'NONE' - return nil - else - raise "Unknown value" - end - end -end diff --git a/lib/MonetDBExceptions.rb b/lib/MonetDBExceptions.rb deleted file mode 100644 index 75745b5144ff680da4a20d97c6fbcaa969efbb40..0000000000000000000000000000000000000000 --- a/lib/MonetDBExceptions.rb +++ /dev/null @@ -1,25 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -# Exception classes for the ruby-monetdb driver - -class MonetDBQueryError < StandardError; end -class MonetDBDataError < StandardError; end -class MonetDBCommandError < StandardError; end -class MonetDBConnectionError < StandardError; end -class MonetDBSocketError < StandardError; end -class MonetDBProtocolError < StandardError; end \ No newline at end of file diff --git a/lib/demo.rb b/lib/demo.rb deleted file mode 100644 index 94fd640a26e980f577b666bf14fc148637e8d5cd..0000000000000000000000000000000000000000 --- a/lib/demo.rb +++ /dev/null @@ -1,101 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -require 'MonetDB' - -db = MonetDB.new - -db.conn({ :user => "monetdb", :passwd => "monetdb", :port => 50000, :language => "sql", :host => "localhost", :database => "ruby_test", :auth_type => "SHA1" }) - -# set type_cast=true to enable MonetDB to Ruby type mapping -#res = db.query("select * from tables, tables, tables;") - -#db.query("DROP TABLE tests2 ") -#db.query(" CREATE TABLE tests2 ( col1 varchar(255), col2 varchar(255)) " ) - -#puts "Number of rows returned: " + res.num_rows.to_s -#puts "Number of fields: " + res.num_fields.to_s - -# Get the columns' name -# print res.name_fields - -###### Fetch all rows and store them - -#puts res.fetch_all - - -# Iterate over the record set and retrieve on row at a time -#puts res.fetch -#while row = res.fetch do -# printf "%s \n", row -#end - - -###### Get all records and hash them by column name -#row = res.fetch_all_hash() - -#puts col_names[0] + "\t\t" + col_names[1] -#0.upto(res.num_rows) { |i| -# puts row['id'][i] -#} - - -###### Iterator over columns (on cell at a time) - -#while row = res.fetch_hash do -# printf "%s\n", row["id"] -#end - -# SQL TRANSACTIONS and SAVE POINTS - - - -db.query('DROP TABLE tests2') -db.auto_commit(false) -puts db.auto_commit? -# create a savepoint -db.save -db.query("CREATE TABLE tests2 (col1 VARCHAR(255), col2 VARCHAR(255))") -res = db.query("SAVEPOINT #{db.transactions} ;") -res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -#res = db.query("INSERT INTO \"tests2\" VALUES ('€¿®µ¶¹', '€¿®µ¶¹')") -db.query("COMMIT") -db.release - -db.save -res = db.query("SAVEPOINT #{db.transactions} ;") -res = db.query("INSERT INTO \"tests2\" VALUES('NAME4', 'SURNAME4')") - -res = db.query("ROLLBACK TO SAVEPOINT #{db.transactions};") -db.release - -db.auto_commit(true) -puts db.auto_commit? -res = db.query('SELECT * from tests2') -while row = res.fetch do - printf "%s \n", row -end - -res.free - -db.close diff --git a/lib/hasher.rb b/lib/hasher.rb deleted file mode 100644 index 2501266dc54e34bdc0f5bee433b8f821a675f7db..0000000000000000000000000000000000000000 --- a/lib/hasher.rb +++ /dev/null @@ -1,56 +0,0 @@ -# The contents of this file are subject to the MonetDB Public License -# Version 1.1 (the "License"); you may not use this file except in -# compliance with the License. You may obtain a copy of the License at -# http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -# License for the specific language governing rights and limitations -# under the License. -# -# The Original Code is the MonetDB Database System. -# -# The Initial Developer of the Original Code is CWI. -# Portions created by CWI are Copyright (C) 1997-July 2008 CWI. -# Copyright August 2008-2011 MonetDB B.V. -# All Rights Reserved. - -require 'digest/md5' -require 'digest/sha1' -require 'digest/sha2' - -class Hasher - # Constructor - # method = "SHA1" or "MD5" - # pwd = Password - def initialize(method, pwd) - if (method.upcase == "SHA1") - @hashfunc = Digest::SHA1.new - @hashname = method.upcase - elsif (method.upcase == "SHA256") - @hashfunc = Digest::SHA256.new - @hashname = method.upcase - elsif (method.upcase == "SHA384") - @hashfunc = Digest::SHA384.new - @hashname = method.upcase - elsif (method.upcase == "SHA512") - @hashfunc = Digest::SHA512.new - @hashname = method.upcase - else - # default to MD5 - @hashfunc = Digest::MD5.new - @hashname = "MD5" - end - @pwd = pwd - end - - - def hashname - @hashname - end - - # Compute hash code - def hashsum - return @hashfunc.hexdigest(@pwd) - end -end diff --git a/lib/test/test_capabilities.rb b/lib/test/test_capabilities.rb deleted file mode 100644 index fc2146b2d715795ca9fed5be7d081c1b7b38df90..0000000000000000000000000000000000000000 --- a/lib/test/test_capabilities.rb +++ /dev/null @@ -1,310 +0,0 @@ -# unit test suite for monetdb. -# connects to the 'ruby_test' database and runs test on the server capabilities and SQL language. -# Create first a database with the command: -# $ monetdb create ruby_test -# $ monetdb start ruby_start -# -# Tests examples have been taken from the python and java internfaces and mysql driver. - -require 'MonetDB' -require 'test/unit' - -require 'time' -require 'date' - - -class TC_MonetDBCapabilities < Test::Unit::TestCase - # ruby rand function does not support MIN..MAX bounds. - # This alias adds that feature. - alias original_rand rand - def rand(arg1=nil, arg2=nil) - if !arg1.kind_of?(Enumerable) && arg2 == nil - original_rand(arg1) - elsif arg1.kind_of? Enumerable - as_array = arg1.to_a - as_array[original_rand(as_array.length)] - elsif arg1 != nil - arg1 + original_rand(arg2) - end - end - - # check the existance of a table - def table_exists?(table='test_ruby') - begin - res = @db.query("select * from #{table} where 1=0") - return true - rescue - return false - end - end - - def drop_table(table='test_ruby') - res = @db.query("DROP TABLE #{table}") - end - - def setup - @db = MonetDB.new - @db.connect(user = "monetdb", passwd = "monetdb", lang = "sql", host="localhost", port = 50000, db_name = "ruby_test", auth_type = "SHA1") - - end - - def teardown - @db.close - end - - # CREATE TABLE test - def test_create_table(table='test_ruby', cols = [ "First_Name varchar(255)", "Second_Name varchar(255)"]) - - if table_exists?(table) - drop_table(table) - end - - colsdef = "" - cols.each do |c| colsdef += c + ',' end - - colsdef = colsdef.chop # remove last ',' character - - - res = @db.query('CREATE TABLE ' + table + ' (' + colsdef + ')') - end - - - # perform an inserstion of 'data' into 'table' and check the resulting - # length - def test_data_integrity(table='test_ruby', data=["Gabriele", "MODENA"]) - test_create_table - - values = "" - data.each do |d| values += '\'' + d.to_s + '\'' + ',' end - values = values.chop # remove last ',' character - - insert = 'INSERT INTO ' + table + ' VALUES (' + values + ' )' - - @db.query(insert) - - res = @db.query("SELECT * FROM #{table}") - rows = res.fetch_all - - assert_equal(res.num_rows, rows.size) - end - - # test TRANSACTION, COMMIT, ROLLBACK and SAVEPOINT in auto_commit=off mode - def test_transactions(table="test_monetdb_transactions", columndefs=['col1 INT', 'col2 VARCHAR(255)']) - test_create_table(table, columndefs) - - data = [1, 'aa'] - values = "" - - data.each do |d| values += '\'' + d.to_s + '\'' + ',' end - values = values.chop # remove last ',' character - - insert = "INSERT INTO " + table + " VALUES " + " ( " + values + " )" - - @db.query('START TRANSACTION') - @db.auto_commit(flag=false) # if @db.auto_commit? - @db.query(insert) - - @db.query("COMMIT") - - res = @db.query('SELECT * FROM ' + table) - rows_committed = res.fetch_all - res.free - - # create a save point - @db.save - @db.query("SAVEPOINT #{@db.transactions} ;") - - @db.query(insert) - - # rollback to savepoint - @db.query("ROLLBACK TO SAVEPOINT #{@db.transactions};") - @db.release - - res = @db.query('SELECT * FROM ' + table) - rows_rolled_back = res.fetch_all - res.free - - assert_equal(rows_committed, rows_rolled_back) - - # restore autocommit for remaining tests - @db.auto_commit(flag=true) - end - - # tests on datatypes conversion - def test_char(table="test_monetdb_char", coldefs=["char_field CHAR(1)"]) - test_create_table(table, coldefs) - char = 'a' - - @db.query("INSERT INTO " + table + " VALUES ( '" + char +"' ) ") - - res = @db.query("SELECT char_field FROM " + table + " where char_field = '" + char +"'") - stored_string = res.fetch_hash - - assert_equal(char, stored_string['char_field']) - end - - def test_smallint(table="test_monetdb_smallint", coldefs=["int_field SMALLINT"]) - test_create_table(table, coldefs) - - original_num = rand(-32768, 32767) - num = original_num.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + num +"') ") - - res = @db.query("SELECT int_field FROM " + table + " where int_field = '" + num +"'") - - stored_string = res.fetch_hash - - assert_equal(num.to_i, stored_string['int_field'].getInt) - end - - def test_int(table="test_monetdb_int", coldefs=["int_field INT"]) - test_create_table(table, coldefs) - - original_num = rand((2 ** 31 -1)) - num = original_num.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + num +"') ") - - res = @db.query("SELECT int_field FROM " + table + " where int_field = '" + num +"'") - - stored_string = res.fetch_hash - - assert_equal(original_num, stored_string['int_field'].getInt) - end - - def test_bigint(table="test_monetdb_bigint", coldefs=["int_field BIGINT"]) - test_create_table(table, coldefs) - - original_num = rand((2 ** 63 -1)) - num = original_num.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + num +"') ") - - res = @db.query("SELECT int_field FROM " + table + " where int_field = '" + num +"'") - - stored_string = res.fetch_hash - - assert_equal(original_num, stored_string['int_field'].getInt) - end - - def test_real(table="test_monetdb_real", coldefs=["float_field REAL"]) - test_create_table(table, coldefs) - - original_num = 1.6065851e+20 - num = original_num.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + num +"') ") - - res = @db.query("SELECT float_field FROM " + table + " where float_field = '" + num +"'") - - stored_string = res.fetch_hash - - assert_equal(original_num, stored_string['float_field'].getFloat) - end - - def test_double(table="test_monetdb_double", coldefs=["float_field DOUBLE"]) - test_create_table(table, coldefs) - - original_num = 1.6065851e+22 - num = original_num.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + num +"') ") - - res = @db.query("SELECT float_field FROM " + table + " where float_field = '" + num +"'") - - stored_string = res.fetch_hash - - assert_equal(original_num, stored_string['float_field'].getFloat) - end - - def test_boolean(table="test_monetdb_boolean", coldefs=["bool_field BOOLEAN"] ) - test_create_table(table, coldefs) - - original_bool = false - bool = original_bool.to_s - - @db.query("INSERT INTO " + table + " VALUES ('" + bool +"') ") - - res = @db.query("SELECT bool_field FROM " + table + " where bool_field = #{bool}") - stored_string = res.fetch_hash - assert_equal(original_bool, stored_string['bool_field'].getBool) - end - - def test_datetime(table="test_monetdb_datetime", coldefs=["dt_field TIMESTAMP"]) - test_create_table(table, coldefs) - - timestamp = "2009-07-01 15:34:33" - - date = timestamp.split(' ')[0].split('-') - time = timestamp.split(' ')[1].split(':') - - dt = Time.gm(date[0], date[1], date[2], time[0], time[1], time[2]) - - @db.query("INSERT INTO " + table + " VALUES ('" + timestamp +"') ") - - res = @db.query("SELECT dt_field FROM " + table + " where dt_field = '" + timestamp +"'") - stored_string = res.fetch_hash - assert_equal(dt, stored_string['dt_field'].getDateTime) - end - - def test_date(table="test_monetdb_date", coldefs=["dt_field DATE"]) - test_create_table(table, coldefs) - - timestamp = "2009-07-01" - - @db.query("INSERT INTO " + table + " VALUES ('" + timestamp +"') ") - - res = @db.query("SELECT dt_field FROM " + table + " where dt_field = '" + timestamp +"'") - stored_string = res.fetch_hash - assert_equal(timestamp, stored_string['dt_field'].getDate) - end - - def test_time(table="test_monetdb_time", coldefs=["dt_field TIME"]) - test_create_table(table, coldefs) - - timestamp = "15:34:33" - - @db.query("INSERT INTO " + table + " VALUES ('" + timestamp +"') ") - - res = @db.query("SELECT dt_field FROM " + table + " where dt_field = '" + timestamp +"'") - stored_string = res.fetch_hash - assert_equal(timestamp, stored_string['dt_field'].getTime) - end - - def test_blob(table="test_monetdb_blob", coldefs = ["blob_field BLOB"]) - test_create_table(table, coldefs) - - blob = '0000000A146F777BB46B8FBD46AD503A54629C51' - - @db.query("INSERT INTO " + table + " VALUES ('" + blob + "') ") - - res = @db.query("SELECT blob_field FROM " + table + " where blob_field = '#{blob}'") - - stored_string = res.fetch_hash - assert_equal(blob, stored_string['blob_field']) - end - - def test_utf8(table="test_monetdb_utf8", coldefs=["utf8_field varchar(100000)"]) - test_create_table(table, coldefs) - - utf8_string = "€¿®µ¶¹€¿®µ¶¹€¿®µ¶¹" - - @db.query("INSERT INTO " + table + " VALUES ( '#{utf8_string}' ) ") - - res = @db.query("SELECT utf8_field FROM #{table} where utf8_field = '#{utf8_string}' ") - stored_string = res.fetch_hash - - assert_equal(utf8_string, stored_string['utf8_field']) - end - - # test MonetDB::conn() named parameters connection method. - def test_conn_with_named_parameters - db = MonetDB.new() - - db.conn({ :user => "monetdb", :passwd => "monetdb", :port => 50000, :host => "localhost", :database => "ruby_test"}) - assert_equal(true, db.is_connected?) - db.close - end - -end diff --git a/ruby-monetdb-sql-0.1.gemspec b/ruby-monetdb-sql-0.1.gemspec deleted file mode 100644 index e773f0738fc2abb00c259f2a18acc5a9534ab703..0000000000000000000000000000000000000000 --- a/ruby-monetdb-sql-0.1.gemspec +++ /dev/null @@ -1,17 +0,0 @@ - -Gem::Specification.new do |s| - s.required_ruby_version = '>= 2.1.0' - s.name = %q{ruby-monetdb-sql} - s.version = "0.2" - s.date = %q{2009-04-27} - s.authors = ["G Modena"] - s.email = %q{gm@cwi.nl} - s.summary = %q{Pure Ruby database driver for MonetDB/SQL} - s.homepage = %q{http://monetdb.cwi.nl/} - s.description = %q{Pure Ruby database driver for the MonetDB/SQL columnar database management system} - s.files = ["README", "lib/MonetDB.rb", "lib/MonetDBConnection.rb", "lib/MonetDBData.rb", "lib/MonetDBExceptions.rb", "lib/hasher.rb"] - s.has_rdoc = true - s.require_path = './lib' - # placeholder project to avoid warning about not having a rubyforge_project - s.rubyforge_project = "nowarning" -end