HanaDb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4028bc5d3621954bcf5b260fe6ab3340d44142124c8ef32ce5a1e75ebbfb1ad0
4
+ data.tar.gz: 8512458d1fdb80213e810fc0974e7ceb7a714c55d3e98a51008bb0f06f4484f6
5
+ SHA512:
6
+ metadata.gz: 841bba69879a219cfb788992d1035a00bc1384b59c6c7e850d7588ca1e4fd4d33d774a1977acd9ead4e4342efd6426c1910154a63605274492d0ecb0513169f8
7
+ data.tar.gz: f3ced395f25779cf8c53f3f0ecb416298322c41def387b54bf27c6b0260bbcaf6062032c021a3a453ef183e4d067b6e2bedc02083b860a12c5cd4dccedc78391
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at avdhesh51000@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in HanaDb.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
@@ -0,0 +1,31 @@
1
+ require_relative 'lib/HanaDb/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "HanaDb"
5
+ spec.version = HanaDb::VERSION
6
+ spec.authors = ["Avdhesh"]
7
+ spec.email = ["avdhesh51000@gmail.com"]
8
+
9
+ spec.summary = %q{Active record hana_adapter.}
10
+ spec.description = %q{Active record hanaclient adapter to connect with hana db.}
11
+ spec.homepage = "https://github.com/Avdhesh51000/HanaDb"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/Avdhesh51000/HanaDb"
19
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.add_development_dependency 'rails'
28
+ spec.add_runtime_dependency 'hanaclient'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Avdhesh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # HanaDb
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/HanaDb`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'HanaDb'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install HanaDb
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/HanaDb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/HanaDb/blob/master/CODE_OF_CONDUCT.md).
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
41
+
42
+ ## Code of Conduct
43
+
44
+ Everyone interacting in the HanaDb project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/HanaDb/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "HanaDb"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ require "HanaDb/version"
2
+ require "active_record/connection_adapters/hanaclient_adapter"
3
+
4
+ module HanaDb
5
+ class Error < StandardError; end
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,3 @@
1
+ module HanaDb
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ class Column < ConnectionAdapters::Column
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,121 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ module DatabaseStatements
5
+ # Executes the SQL statement in the context of this connection and returns
6
+ # the raw result from the connection adapter.
7
+ def execute(sql, name = nil)
8
+ log(sql, name) do
9
+ if HA.instance.api.hanaclient_execute_immediate(@connection, sql) == 0
10
+ result, errstr = HA.instance.api.hanaclient_error(@connection)
11
+ raise ActiveRecord::StatementInvalid.new(errstr)
12
+ end
13
+ end
14
+ end
15
+
16
+ # Executes +sql+ statement in the context of this connection using
17
+ # +binds+ as the bind substitutes. +name+ is logged along with
18
+ # the executed +sql+ statement.
19
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
20
+ exec_and_clear(sql, name, binds, prepare: prepare) do |stmt|
21
+ record = []
22
+ columns = []
23
+
24
+ max_cols = HA.instance.api.hanaclient_num_cols(stmt)
25
+ if( max_cols > 0 )
26
+ columns = max_cols.times.collect{ |x| HA.instance.api.hanaclient_get_column_info(stmt, x)[2] }
27
+
28
+ while HA.instance.api.hanaclient_fetch_next(stmt) == 1
29
+ result = []
30
+
31
+ max_cols.times do |cols|
32
+ result << HA.instance.api.hanaclient_get_column(stmt, cols)[1]
33
+ end
34
+
35
+ record << result
36
+ end
37
+ end
38
+
39
+ ActiveRecord::Result.new(columns, record)
40
+ end
41
+ end
42
+
43
+ # Executes the truncate statement.
44
+ def truncate(table_name, name = nil)
45
+ exec_query("TRUNCATE TABLE #{quote_table_name(table_name)}", name)
46
+ end
47
+
48
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
49
+ result = exec_query(sql, name, binds)
50
+ unless sequence_name
51
+ table_ref = extract_table_ref_from_insert_sql(sql)
52
+ if table_ref
53
+ pk = primary_key(table_ref) if pk.nil?
54
+ pk = suppress_composite_primary_key(pk)
55
+ sequence_name = default_sequence_name(table_ref, pk)
56
+ end
57
+ return result unless sequence_name
58
+ end
59
+ last_insert_id_result(sequence_name)
60
+ end
61
+
62
+ # Executes delete +sql+ statement in the context of this connection using
63
+ # +binds+ as the bind substitutes. +name+ is logged along with
64
+ # the executed +sql+ statement.
65
+ def exec_delete(sql, name = nil, binds = [])
66
+ exec_and_clear(sql, name, binds) do |stmt|
67
+ HA.instance.api.hanaclient_affected_rows(stmt)
68
+ end
69
+ end
70
+ alias :exec_update :exec_delete
71
+
72
+ def reset_transaction #:nodoc:
73
+ @transaction_manager = ConnectionAdapters::Hanaclient::TransactionManager.new(self)
74
+ end
75
+
76
+ def commit_db_transaction()
77
+ HA.instance.api.hanaclient_commit(@connection)
78
+ end
79
+
80
+ def exec_rollback_db_transaction()
81
+ HA.instance.api.hanaclient_rollback(@connection)
82
+ end
83
+
84
+ def default_sequence_name(table, column)
85
+ query_value(<<-end_sql, "SCHEMA")
86
+ SELECT SEQUENCE_NAME FROM SEQUENCES WHERE SEQUENCE_NAME like ('%' || (SELECT column_id from table_columns where table_name = #{quote(table.gsub('"', ''))} AND column_name = #{quote(column.gsub('"', ''))}) || '%')
87
+ end_sql
88
+ end
89
+
90
+ def insert_fixtures(fixtures, table_name)
91
+ fixtures.each do |fixture|
92
+ insert_fixture(fixture, table_name)
93
+ end
94
+ end
95
+
96
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
97
+ disable_referential_integrity do
98
+ transaction(requires_new: true) do
99
+ tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
100
+
101
+ fixture_set.each do |table_name, rows|
102
+ rows.each { |row| insert_fixture(row, table_name) }
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ # This is not supported. Can't insert an empty column
109
+ def empty_insert_statement_value
110
+ raise NotImplementedError
111
+ end
112
+
113
+ private
114
+ def suppress_composite_primary_key(pk)
115
+ pk unless pk.is_a?(Array)
116
+ end
117
+
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,48 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ module Quoting
5
+
6
+ # Replaces any " symbols with ""
7
+ def quote_column_name(name)
8
+ @quoted_column_names = @quoted_column_names || {}
9
+ @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
10
+ end
11
+
12
+ def quoted_true
13
+ "true".freeze
14
+ end
15
+
16
+ def unquoted_true
17
+ "true".freeze
18
+ end
19
+
20
+ def quoted_false
21
+ "false".freeze
22
+ end
23
+
24
+ def unquoted_false
25
+ "false".freeze
26
+ end
27
+
28
+ def quote_table_name_for_assignment(table, attr)
29
+ quote_column_name(attr)
30
+ end
31
+
32
+ def quote_table_name name
33
+ name.to_s
34
+ end
35
+
36
+ # Gets the time from a date string
37
+ def quoted_time(value)
38
+ quoted_date(value).match(/[0-2][0-9]:[0-9][0-9]:[0-9][0-9]/)[0]
39
+ end
40
+
41
+ def fetch_type_metadata(sql_type)
42
+ Hanaclient::TypeMetadata.new(super(sql_type))
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ def visit_AddColumnDefinition(o)
8
+ "ADD (#{accept(o.column)})"
9
+ end
10
+
11
+ def visit_ChangeColumnDefinition(o)
12
+ change_column_sql = "ALTER (#{accept(o.column)})"
13
+ end
14
+
15
+ def visit_TableDefinition(o)
16
+
17
+ if o.temporary && o.row_table
18
+ table_type = 'GLOBAL TEMPORARY ROW'
19
+ elsif o.temporary
20
+ table_type = 'GLOBAL TEMPORARY COLUMN'
21
+ elsif o.row_table
22
+ table_type = 'ROW'
23
+ else
24
+ table_type = 'COLUMN'
25
+ end
26
+
27
+ create_sql = "CREATE #{table_type} TABLE #{quote_table_name(o.name)} "
28
+
29
+ statements = o.columns.map { |c| accept c }
30
+ statements << accept(o.primary_keys) if o.primary_keys
31
+
32
+ if supports_indexes_in_create?
33
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
34
+ end
35
+
36
+ if supports_foreign_keys_in_create?
37
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
38
+ end
39
+
40
+ create_sql << "(#{statements.join(', ')})" if statements.present?
41
+ add_table_options!(create_sql, table_options(o))
42
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
43
+ create_sql
44
+ end
45
+
46
+ def visit_PrimaryKeyDefinition(o)
47
+ "PRIMARY KEY (#{o.name.map{|name| quote_column_name(name)}.join(', ')})"
48
+ end
49
+
50
+ def add_column_options!(sql, options)
51
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
52
+ # must explicitly check for :null to allow change_column to work on migrations
53
+ if options[:null] == false
54
+ sql << " NOT NULL"
55
+ end
56
+ if options[:primary_key] == true
57
+ sql << " PRIMARY KEY"
58
+ end
59
+ if options[:auto_increment] == true
60
+ sql << " GENERATED BY DEFAULT AS IDENTITY"
61
+ end
62
+ sql
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ class ReferenceDefinition < ActiveRecord::ConnectionAdapters::ReferenceDefinition
5
+ end
6
+
7
+ module ColumnMethods
8
+ def primary_key(name, type = :primary_key, **options)
9
+ options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default)
10
+ super
11
+ end
12
+
13
+ # Generates a function for each supported database type
14
+ [
15
+ :string,
16
+ :integer,
17
+ :float,
18
+ :decimal,
19
+ :time,
20
+ :date,
21
+ :seconddate,
22
+ :timestamp,
23
+ :binary,
24
+ :unicode,
25
+ :text,
26
+ :boolean
27
+ ].each do |column_type|
28
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
29
+ def #{column_type}(*args, **options)
30
+ args.each { |name| column(name, :#{column_type}, options) }
31
+ end
32
+ CODE
33
+ end
34
+ end
35
+
36
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
37
+ include ColumnMethods
38
+
39
+ attr_accessor :indexes
40
+ attr_reader :name, :row_table, :temporary, :options, :as, :foreign_keys, :comment
41
+
42
+ def initialize(name, temporary = false, row_table = false, options = nil, as = nil, comment: nil)
43
+ @columns_hash = {}
44
+ @indexes = []
45
+ @foreign_keys = []
46
+ @primary_keys = nil
47
+ @row_table = row_table
48
+ @temporary = temporary
49
+ @options = options
50
+ @as = as
51
+ @name = name
52
+ @comment = comment
53
+ end
54
+
55
+
56
+ def new_column_definition(name, type, **options)
57
+ if type == :primary_key
58
+ type = :integer
59
+ options[:limit] ||= 8
60
+ options[:auto_increment] = true
61
+ options[:primary_key] = true
62
+ end
63
+ super
64
+ end
65
+ end
66
+
67
+ class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
68
+ end
69
+
70
+ class Table < ActiveRecord::ConnectionAdapters::Table
71
+ include ColumnMethods
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,287 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ module SchemaStatements
5
+
6
+ def create_table(table_name, comment: nil, **options)
7
+ td = create_table_definition table_name, options[:temporary], options[:row_table], options[:options], options[:as], comment: comment
8
+
9
+ if options[:id] != false && !options[:as]
10
+ pk = options.fetch(:primary_key) do
11
+ Base.get_primary_key table_name.to_s.singularize
12
+ end
13
+
14
+ if pk.is_a?(Array)
15
+ td.primary_keys pk
16
+ else
17
+ td.primary_key pk, options.fetch(:id, :primary_key), options
18
+ end
19
+ end
20
+
21
+ yield td if block_given?
22
+
23
+ if options[:force]
24
+ drop_table(table_name, **options, if_exists: true)
25
+ end
26
+
27
+ result = execute schema_creation.accept td
28
+
29
+ unless supports_indexes_in_create?
30
+ td.indexes.each do |column_name, index_options|
31
+ add_index(table_name, column_name, index_options)
32
+ end
33
+ end
34
+
35
+ if supports_comments? && !supports_comments_in_create?
36
+ change_table_comment(table_name, comment) if comment.present?
37
+
38
+ td.columns.each do |column|
39
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
40
+ end
41
+ end
42
+
43
+ result
44
+ end
45
+
46
+ # Returns an array of IndexDefinition objects for the given table.
47
+ def indexes(table_name, name = nil)
48
+ if name
49
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
50
+ Passing name to #indexes is deprecated without replacement.
51
+ MSG
52
+ end
53
+
54
+ scope = quoted_scope(table_name)
55
+
56
+ sql = "SELECT TABLE_NAME, INDEX_NAME, CONSTRAINT, COLUMN_NAME FROM SYS.INDEX_COLUMNS WHERE TABLE_NAME = #{scope[:schema]} AND SCHEMA_NAME = #{scope[:name]}"
57
+ exec_and_clear(sql, "SCHEMA") do |stmt|
58
+ index_hashes = {}
59
+ while HA.instance.api.hanaclient_fetch_next(stmt) == 1
60
+ table_name = HA.instance.api.hanaclient_get_column(stmt, 0)[1]
61
+ index_name = HA.instance.api.hanaclient_get_column(stmt, 1)[1]
62
+ constraint = HA.instance.api.hanaclient_get_column(stmt, 2)[1]
63
+ column_name = HA.instance.api.hanaclient_get_column(stmt, 3)[1]
64
+ next if constraint.to_s.scan(/PRIMARY KEY/)
65
+
66
+ index_hashes[index_name] ||= IndexDefinition.new(table_name, index_name, constraint.to_s.scan(/UNIQUE/) ? true : false, [], {}, nil, nil, nil, nil, nil)
67
+ index_hashes[index_name].columns << column_name
68
+ end
69
+
70
+ index_hashes.values
71
+ end
72
+ end
73
+
74
+ def rename_table(table_name, new_name)
75
+ execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
76
+ rename_table_indexes(table_name, new_name)
77
+ end
78
+
79
+ def drop_table(table_name, options = {})
80
+ begin
81
+ execute "DROP TABLE #{quote_table_name(table_name)} CASCADE"
82
+ rescue ActiveRecord::StatementInvalid => e
83
+ raise e unless options[:if_exists]
84
+ end
85
+ end
86
+
87
+ def change_column(table_name, column_name, type, options = {})
88
+ column = column_for(table_name, column_name)
89
+
90
+ unless options.key?(:default)
91
+ options[:default] = column.default
92
+ end
93
+
94
+ unless options.key?(:null)
95
+ options[:null] = column.null
96
+ end
97
+
98
+ unless options.key?(:comment)
99
+ options[:comment] = column.comment
100
+ end
101
+
102
+ td = create_table_definition(table_name)
103
+ cd = td.new_column_definition(column.name, type, options)
104
+ change_column_sql = schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
105
+
106
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql}")
107
+ end
108
+
109
+ def change_column_default(table_name, column_name, default_or_changes)
110
+ default = extract_new_default_value(default_or_changes)
111
+ column = column_for(table_name, column_name)
112
+ change_column(table_name, column_name, column.sql_type, default: default)
113
+ end
114
+
115
+ def change_column_null(table_name, column_name, null, default = nil)
116
+ column = column_for(table_name, column_name)
117
+
118
+ unless null || default.nil?
119
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
120
+ end
121
+
122
+ change_column(table_name, column_name, column.sql_type, null: null)
123
+ end
124
+
125
+ def rename_column(table_name, column_name, new_column_name)
126
+ execute("RENAME COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}")
127
+ end
128
+
129
+ def remove_index(table_name, options = {})
130
+ index_name = index_name_for_remove(table_name, options)
131
+ execute("DROP INDEX #{quote_column_name(index_name)}")
132
+ end
133
+
134
+ def rename_index(table_name, old_name, new_name)
135
+ validate_index_length!(table_name, new_name)
136
+ execute("RENAME INDEX #{quote_column_name(old_name)} TO #{quote_column_name(new_name)}")
137
+ end
138
+
139
+ # Returns an array of ForeignKeyDefinitions for a given table
140
+ def foreign_keys(table_name)
141
+ raise ArgumentError unless table_name.present?
142
+
143
+ scope = quoted_scope(table_name)
144
+
145
+ fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
146
+ SELECT REFERENCED_TABLE_NAME AS "to_table",
147
+ REFERENCED_COLUMN_NAME AS "primary_key",
148
+ COLUMN_NAME AS "column",
149
+ CONSTRAINT_NAME AS "name",
150
+ UPDATE_RULE AS "on_update",
151
+ DELETE_RULE AS "on_delete"
152
+ FROM SYS.REFERENTIAL_CONSTRAINTS
153
+ WHERE SCHEMA_NAME = #{scope[:schema]}
154
+ AND TABLE_NAME = #{scope[:name]}
155
+ SQL
156
+
157
+ fk_info.map do |row|
158
+ options = {
159
+ column: row["column"],
160
+ name: row["name"],
161
+ primary_key: row["primary_key"]
162
+ }
163
+
164
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
165
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
166
+
167
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
168
+ end
169
+ end
170
+
171
+ def extract_foreign_key_action(specifier)
172
+ case specifier
173
+ when "CASCADE"; :cascade
174
+ when "SET NULL"; :nullify
175
+ end
176
+ end
177
+
178
+ def change_table_comment(table_name, comment)
179
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
180
+ end
181
+
182
+ def change_column_comment(table_name, column_name, comment)
183
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
184
+ end
185
+
186
+ # Returns the sql for a given type
187
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
188
+ type = type.to_sym if type
189
+ if native = native_database_types[type]
190
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
191
+
192
+ if type == :integer
193
+ case limit
194
+ when 1
195
+ column_type_sql = "TINYINT"
196
+ when 2
197
+ column_type_sql = "SMALLINT"
198
+ when nil, 3, 4
199
+ column_type_sql = "INTEGER"
200
+ when 5..8
201
+ column_type_sql = "BIGINT"
202
+ else
203
+ raise(ActiveRecordError, "No integer type has byte size #{limit}.")
204
+ end
205
+ elsif type == :float
206
+ case limit
207
+ when nil, 1..24
208
+ column_type_sql = "REAL"
209
+ when 25..53
210
+ column_type_sql = "DOUBLE"
211
+ else
212
+ raise(ActiveRecordError, "No float type has byte size #{limit}.")
213
+ end
214
+ elsif type == :decimal # ignore limit, use precision and scale
215
+ scale ||= native[:scale]
216
+
217
+ if precision ||= native[:precision]
218
+ if scale
219
+ column_type_sql << "(#{precision},#{scale})"
220
+ else
221
+ column_type_sql << "(#{precision})"
222
+ end
223
+ elsif scale
224
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
225
+ end
226
+ elsif type == :binary
227
+ if limit.nil? || limit.between?(1,5000)
228
+ column_type_sql = "VARBINARY(#{limit ? limit : 5000})"
229
+ else
230
+ column_type_sql = "BLOB"
231
+ end
232
+ elsif type == :unicode
233
+ if limit.nil? || limit.between?(1,5000)
234
+ column_type_sql = "NVARCHAR(#{limit ? limit : 5000})"
235
+ else
236
+ column_type_sql = "NCLOB"
237
+ end
238
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
239
+ column_type_sql << "(#{limit})"
240
+ end
241
+
242
+ column_type_sql.upcase
243
+ else
244
+ type.to_s.upcase
245
+ end
246
+ end
247
+
248
+ private
249
+
250
+ def data_source_sql(name = nil, type: nil)
251
+ scope = quoted_scope(name, type: type)
252
+
253
+ table_sql = "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = #{scope[:schema]}"
254
+ table_sql << " AND TABLE_NAME = #{scope[:name]}" if scope[:name]
255
+
256
+ view_sql = "SELECT VIEW_NAME AS TABLE_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = #{scope[:schema]}"
257
+ view_sql << " AND VIEW_NAME = #{scope[:name]}" if scope[:name]
258
+
259
+ case type
260
+ when "BASE TABLE"
261
+ return table_sql
262
+ when "VIEW"
263
+ return view_sql
264
+ end
265
+
266
+ "#{table_sql} UNION ALL #{view_sql}"
267
+ end
268
+
269
+ def quoted_scope(name = nil, type: nil)
270
+ schema, name = extract_schema_qualified_name(name)
271
+ scope = {}
272
+ scope[:schema] = schema ? quote(schema) : "CURRENT_SCHEMA"
273
+ scope[:name] = quote(name) if name
274
+ scope
275
+ end
276
+
277
+ # Extracts the schema and table name from a string of the form "schema.table"
278
+ def extract_schema_qualified_name(string)
279
+ schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/)
280
+ schema, name = nil, schema unless name
281
+ [schema, name]
282
+ end
283
+
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ class TransactionManager < ActiveRecord::ConnectionAdapters::TransactionManager
5
+ def begin_transaction(options = {})
6
+ @connection.lock.synchronize do
7
+ run_commit_callbacks = !current_transaction.joinable?
8
+ # HANA does not support savepoints
9
+ transaction = RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
10
+
11
+ @stack.push(transaction)
12
+ transaction
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Hanaclient
4
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
5
+ def initialize(type_metadata)
6
+ super(type_metadata)
7
+ @type_metadata = type_metadata
8
+ end
9
+
10
+ def ==(other)
11
+ other.is_a?(Hanaclient::TypeMetadata) &&
12
+ attributes_for_hash == other.attributes_for_hash
13
+ end
14
+ alias eql? ==
15
+
16
+ def hash
17
+ attributes_for_hash.hash
18
+ end
19
+
20
+ protected
21
+
22
+ def attributes_for_hash
23
+ [self.class, @type_metadata]
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,278 @@
1
+ require "singleton"
2
+
3
+ require 'arel/visitors/hanaclient'
4
+
5
+ require "active_record/connection_adapters/abstract_adapter"
6
+ require "active_record/connection_adapters/statement_pool"
7
+
8
+ require "active_record/connection_adapters/hanaclient/column"
9
+ require "active_record/connection_adapters/hanaclient/database_statements"
10
+ require "active_record/connection_adapters/hanaclient/quoting"
11
+ require "active_record/connection_adapters/hanaclient/schema_creation"
12
+ require "active_record/connection_adapters/hanaclient/schema_definitions"
13
+ require "active_record/connection_adapters/hanaclient/schema_statements"
14
+ require "active_record/connection_adapters/hanaclient/transaction"
15
+ require "active_record/connection_adapters/hanaclient/type_metadata"
16
+
17
+ require "hanaclient"
18
+ # Singleton class to hold a valid instance of the HANACLIENTInterface across all connections
19
+ class HA
20
+ include Singleton
21
+ attr_accessor :api
22
+
23
+ @@dbcapi_path = nil
24
+ def self.dbcapi_path= path
25
+ @@dbcapi_path = path
26
+ end
27
+
28
+ def initialize
29
+ @api = HANACLIENT::HANACLIENTInterface.new()
30
+ HANACLIENT::API.hanaclient_initialize_interface(@api, @@dbcapi_path)
31
+ raise LoadError, "Could not initialize HANA client library" if @api.hanaclient_init() == 0
32
+ ObjectSpace.define_finalizer(self, HA.finalize(@api))
33
+ end
34
+
35
+ # Returns a proc that is run when this object is destroyed
36
+ def self.finalize(api)
37
+ proc{
38
+ api.hanaclient_fini()
39
+ HANACLIENT::API.hanaclient_finalize_interface( api )
40
+ }
41
+ end
42
+ end
43
+
44
+ module ActiveRecord
45
+ module ConnectionHandling
46
+ # Establishes a connection to the database that's used by all Active Record objects
47
+ def hanaclient_connection(config)
48
+ HA.dbcapi_path = config[:dbcapi_path]
49
+
50
+ db = HA.instance.api.hanaclient_new_connection()
51
+
52
+ connection_string = "SERVERNODE=#{config[:server]}"
53
+ connection_string += ":#{config[:port]}" if config[:port]
54
+ connection_string += ";UID=#{config[:username]};PWD=#{config[:password]};"
55
+ connection_string += "DATABASENAME=#{config[:database]};" if config[:database]
56
+ # overrides the database option in connection properties if the database option is explicity given
57
+ connection_string += (config[:database] ? config[:connection_properties].gsub(/databasename=[^;]*;/i, "") : config[:connection_properties]) if config[:connection_properties]
58
+
59
+ ConnectionAdapters::HanaclientAdapter.new(db, logger, connection_string, config)
60
+ end
61
+ end
62
+
63
+ module ConnectionAdapters
64
+
65
+ class HanaclientAdapter < AbstractAdapter
66
+ ADAPTER_NAME = "Hanaclient".freeze
67
+
68
+ include ActiveRecord::ConnectionAdapters::Hanaclient::DatabaseStatements
69
+ include ActiveRecord::ConnectionAdapters::Hanaclient::SchemaStatements
70
+ include ActiveRecord::ConnectionAdapters::Hanaclient::Quoting
71
+
72
+ # Supports all standard activerecord types and unicode
73
+ NATIVE_DATABASE_TYPES = {
74
+ primary_key: "BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY",
75
+ string: { name: "VARCHAR", limit: 3000 },
76
+ integer: { name: "INTEGER", limit: 8 },
77
+ float: { name: "FLOAT", limit: 53 },
78
+ decimal: { name: "DECIMAL" },
79
+ time: { name: "TIME" },
80
+ date: { name: "DATE" },
81
+ seconddate: { name: "SECONDDATE" },
82
+ timestamp: { name: "TIMESTAMP" },
83
+ binary: { name: "VARBINARY", limit: 3000 },
84
+ unicode: { name: "NVARCHAR", limit: 3000 },
85
+ text: { name: "CLOB" },
86
+ boolean: { name: "BOOLEAN" }
87
+ }
88
+
89
+ class StatementPool < ConnectionAdapters::StatementPool
90
+ private
91
+
92
+ def dealloc(stmt)
93
+ HA.instance.api.hanaclient_free_stmt(stmt)
94
+ end
95
+ end
96
+
97
+ def schema_creation
98
+ Hanaclient::SchemaCreation.new self
99
+ end
100
+
101
+ def arel_visitor
102
+ Arel::Visitors::Hanaclient.new(self)
103
+ end
104
+
105
+ def initialize( connection, logger, connection_string, config) #:nodoc:
106
+ super(connection, logger, config)
107
+
108
+ @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
109
+
110
+ @connection_string = connection_string
111
+
112
+ @affected_rows = 0
113
+ connect!
114
+ end
115
+
116
+ def supports_foreign_keys?
117
+ true
118
+ end
119
+
120
+ def active?
121
+ result = HA.instance.api.hanaclient_execute_immediate(@connection, "SELECT 1 FROM DUMMY") == 1
122
+ end
123
+
124
+ def connect!
125
+ result = HA.instance.api.hanaclient_connect(@connection, @connection_string)
126
+ unless result == 1
127
+ result, error = HA.instance.api.hanaclient_error(@connection)
128
+ raise ActiveRecord::StatementInvalid.new(error)
129
+ end
130
+ end
131
+
132
+ def reconnect!
133
+ super
134
+ disconnect!
135
+ connect!
136
+ end
137
+
138
+ def disconnect!
139
+ super
140
+ HA.instance.api.hanaclient_disconnect( @connection )
141
+ end
142
+
143
+ def clear_cache!
144
+ @statements.clear
145
+ end
146
+
147
+ def native_database_types
148
+ NATIVE_DATABASE_TYPES
149
+ end
150
+
151
+ # Creates a new column object given a hanaclient response statement (field)
152
+ def new_column_from_field(table_name, field)
153
+ name = field[3]
154
+ default = field[11]
155
+ sql_type_metadata = fetch_type_metadata(field[6])
156
+ nullable = field[10] == "TRUE"
157
+ table_name = field[1]
158
+ collation = field[12]
159
+ Hanaclient::Column.new(name, default, sql_type_metadata, nullable, table_name, nil, collation, comment: nil)
160
+ end
161
+
162
+ # Returns the primary keys of a given table
163
+ def primary_keys(table_name)
164
+ raise ArgumentError unless table_name.present?
165
+
166
+ scope = quoted_scope(table_name)
167
+
168
+ column_names = query_values(<<-SQL.strip_heredoc, "SCHEMA")
169
+ SELECT COLUMN_NAME
170
+ FROM SYS.INDEX_COLUMNS
171
+ WHERE CONSTRAINT = 'PRIMARY KEY'
172
+ AND SCHEMA_NAME = #{scope[:schema]}
173
+ AND TABLE_NAME = #{scope[:name]}
174
+ ORDER BY POSITION
175
+ SQL
176
+
177
+ column_names.map{|name| name}
178
+ end
179
+
180
+ private
181
+ def initialize_type_map(m)
182
+ super
183
+ m.register_type %r(date)i, Type::Date.new
184
+ m.register_type %r(time)i, Type::Time.new
185
+ m.register_type %r(seconddate)i, Type::DateTime.new
186
+ m.register_type %r(timestamp)i, Type::DateTime.new
187
+
188
+ m.register_type %r(bigint)i, Type::Integer.new(limit: 8)
189
+ m.register_type %r(integer)i, Type::Integer.new(limit: 4)
190
+ m.register_type %r(smallint)i, Type::Integer.new(limit: 2)
191
+ m.register_type %r(tinyint)i, Type::UnsignedInteger.new(limit: 1)
192
+
193
+ m.register_type %r(decimal)i, Type::Decimal.new
194
+ m.register_type %r(real)i, Type::Float.new
195
+ m.register_type %r(double)i, Type::Float.new
196
+
197
+ m.register_type %r(boolean)i, Type::Boolean.new
198
+
199
+ m.register_type %r(varchar)i, Type::String.new(limit: 5000)
200
+ m.register_type %r(nvarchar)i, Type::String.new(limit: 5000)
201
+ m.register_type %r(alphanum)i, Type::String.new(limit: 127)
202
+ m.register_type %r(varbinary)i, Type::Binary.new(limit: 5000)
203
+
204
+ m.register_type %r(blob)i, Type::Text.new(limit: 2**31 - 1)
205
+ m.register_type %r(clob)i, Type::Text.new(limit: 2**31 - 1)
206
+ m.register_type %r(nlob)i, Type::Text.new(limit: 2**31 - 1)
207
+ end
208
+
209
+ # Returns column information for a given table
210
+ def column_definitions(table_name)
211
+ scope = quoted_scope(table_name)
212
+
213
+ query(<<-end_sql, "SCHEMA")
214
+ SELECT * FROM SYS.TABLE_COLUMNS WHERE TABLE_NAME = #{scope[:name]} AND SCHEMA_NAME = #{scope[:schema]} ORDER BY POSITION
215
+ end_sql
216
+ end
217
+
218
+ def create_table_definition(*args)
219
+ Hanaclient::TableDefinition.new(*args)
220
+ end
221
+
222
+ def extract_table_ref_from_insert_sql(sql)
223
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
224
+ $1.strip if $1
225
+ end
226
+
227
+ def last_insert_id_result(sequence_name)
228
+ exec_query("SELECT #{quote_table_name(sequence_name)}.CURRVAL FROM DUMMY", "SQL")
229
+ end
230
+
231
+ # Executes an sql statement and frees the statement
232
+ def exec_and_clear(sql, name = "SQL", binds = [], prepare: false)
233
+ type_casted_binds = type_casted_binds(binds)
234
+
235
+ log(sql, name, binds, type_casted_binds) do
236
+ cached = false
237
+
238
+ # Statement caching seems to have issues. Don't use for now
239
+ # if without_prepared_statement?(binds)
240
+ stmt = HA.instance.api.hanaclient_prepare(@connection, sql)
241
+ if stmt.nil?
242
+ result, errstr = HA.instance.api.hanaclient_error(@connection)
243
+ raise ActiveRecord::StatementInvalid.new(errstr)
244
+ end
245
+ # else
246
+ # unless @statements.key? sql
247
+ # @statements[sql] = HA.instance.api.hanaclient_prepare(@connection, sql)
248
+ # if @statements[sql].nil?
249
+ # result, errstr = HA.instance.api.hanaclient_error(@connection)
250
+ # raise ActiveRecord::StatementInvalid.new(errstr)
251
+ # end
252
+ # end
253
+ # stmt = @statements[sql]
254
+ # cached = true
255
+ # end
256
+
257
+ num_params = HA.instance.api.hanaclient_num_params(stmt)
258
+
259
+ num_params.times do |i|
260
+ res, param = HA.instance.api.hanaclient_describe_bind_param(stmt, i)
261
+ param.set_value(type_casted_binds[i])
262
+ HA.instance.api.hanaclient_bind_param(stmt, i, param)
263
+ end
264
+
265
+ if HA.instance.api.hanaclient_execute(stmt) == 0
266
+ result, errstr = HA.instance.api.hanaclient_error(@connection)
267
+ raise ActiveRecord::StatementInvalid.new(errstr)
268
+ end
269
+
270
+ ret = yield stmt
271
+ HA.instance.api.hanaclient_free_stmt(stmt) unless cached
272
+ ret
273
+ end
274
+ end
275
+
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,14 @@
1
+ module Arel
2
+ module Visitors
3
+ class Hanaclient < Arel::Visitors::ToSql
4
+ private
5
+ def visit_Arel_Nodes_As o, collector
6
+ collector = visit o.left, collector
7
+ collector << " AS "
8
+ # The alias must be quoted
9
+ o.right = Arel::Nodes::SqlLiteral.new(quote_column_name(o.right.to_s)) if o.right.instance_of?(Arel::Nodes::SqlLiteral)
10
+ visit o.right, collector
11
+ end
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: HanaDb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Avdhesh
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-01-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hanaclient
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Active record hanaclient adapter to connect with hana db.
42
+ email:
43
+ - avdhesh51000@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - CODE_OF_CONDUCT.md
50
+ - Gemfile
51
+ - HanaDb.gemspec
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/setup
57
+ - gems/activerecord-hanaclient-adapter-2.6.64.gem
58
+ - gems/hanaclient-2.6.64-x86_64-linux.gem
59
+ - lib/HanaDb.rb
60
+ - lib/HanaDb/version.rb
61
+ - lib/active_record/connection_adapters/hanaclient/column.rb
62
+ - lib/active_record/connection_adapters/hanaclient/database_statements.rb
63
+ - lib/active_record/connection_adapters/hanaclient/quoting.rb
64
+ - lib/active_record/connection_adapters/hanaclient/schema_creation.rb
65
+ - lib/active_record/connection_adapters/hanaclient/schema_definitions.rb
66
+ - lib/active_record/connection_adapters/hanaclient/schema_statements.rb
67
+ - lib/active_record/connection_adapters/hanaclient/transaction.rb
68
+ - lib/active_record/connection_adapters/hanaclient/type_metadata.rb
69
+ - lib/active_record/connection_adapters/hanaclient_adapter.rb
70
+ - lib/arel/visitors/hanaclient.rb
71
+ homepage: https://github.com/Avdhesh51000/HanaDb
72
+ licenses:
73
+ - MIT
74
+ metadata:
75
+ allowed_push_host: https://rubygems.org/
76
+ homepage_uri: https://github.com/Avdhesh51000/HanaDb
77
+ source_code_uri: https://github.com/Avdhesh51000/HanaDb
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 2.3.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.1.4
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Active record hana_adapter.
97
+ test_files: []