snowflake_rb 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f52d1d74097341acc40cf111a11d5ab1bf403eb59e14dbf0405b78fa4a51d067
4
+ data.tar.gz: e1fde37704e432fb3ec95825b51c57490e8535f303a9891ece31bc0545187feb
5
+ SHA512:
6
+ metadata.gz: 81351cbcddcedc5228d22689e7d6bce667a57cf4640e50286da04c91c6feaa00f5c2747d3cc3264ef1238c8f0270045232c9c03d53d70ff16da958bdf3c62ba7
7
+ data.tar.gz: cfba487d85753ae881613ff6daf91c0d0713d409d93f8f09eb0f45e46258709152a0f360d0efb5f15d39aa0b3548e503f92dba1df34b79b18108167f90d878cd
Binary file
Binary file
Binary file
Binary file
data/ext/snowflake.so ADDED
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-configurable'
4
+
5
+ module SnowflakeRB
6
+ module Configuration
7
+ extend Dry::Configurable
8
+
9
+ setting :account
10
+ setting :warehouse
11
+ setting :database
12
+ setting :schema
13
+ setting :user
14
+ setting :password
15
+ setting :role
16
+ setting :port, default: 443
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SnowflakeRB
4
+ module Extension
5
+ def self.env_specific_extension_path
6
+ "../ext/snowflake-#{os}-#{arch}.so"
7
+ end
8
+
9
+ def self.os
10
+ case RUBY_PLATFORM
11
+ when /.*linux.*/
12
+ 'linux'
13
+ when /.*darwin.*/
14
+ 'darwin'
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ def self.arch
21
+ case RUBY_PLATFORM
22
+ when /.*x86_64.*/
23
+ 'amd64'
24
+ when /.*arm64.*/
25
+ 'arm64'
26
+ else
27
+ nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,164 @@
1
+ require 'ffi'
2
+ require 'snowflake_rb/extension'
3
+ require 'snowflake_rb/configuration'
4
+
5
+ # Note: this library is not thread safe as it caches the last error
6
+ # The call pattern expectation is to call last_error after any call which may have gotten an error. If last_error is
7
+ # `nil`, there was no error.
8
+ module Snowflake
9
+ module LibC
10
+ extend FFI::Library
11
+ ffi_lib(FFI::Library::LIBC)
12
+
13
+ attach_function(:free, [:pointer], :void)
14
+ end
15
+
16
+ module Binding
17
+ extend FFI::Library
18
+
19
+ POINTER_SIZE = FFI.type_size(:pointer)
20
+
21
+ ffi_lib(File.expand_path(SnowflakeRB::Extension.env_specific_extension_path, __dir__))
22
+ attach_function(:last_error, 'LastError', [], :strptr)
23
+ # ugh, `port` in gosnowflake is just :int; however, ruby - ffi -> go is passing 32bit int if I just decl :int.
24
+ attach_function(:connect, 'Connect', %i[string string string string string string string int64], :pointer)
25
+ attach_function(:close, 'Close', [:pointer], :void)
26
+ attach_function(:exec, 'Exec', %i[pointer string], :int64)
27
+ attach_function(:fetch, 'Fetch', %i[pointer string], :pointer)
28
+ attach_function(:next_row, 'NextRow', [:pointer], :pointer)
29
+ attach_function(:query_columns, 'QueryColumns', [:pointer], :pointer)
30
+ attach_function(:query_column_count, 'QueryColumnCount', [:pointer], :int32)
31
+ end
32
+
33
+ module Client
34
+ extend SnowflakeRB::Configuration
35
+ module_function
36
+
37
+ # @return String last error or nil. May be end of file which is not really an error
38
+ def last_error
39
+ error, cptr = ::Snowflake::Binding.last_error
40
+ LibC.free(cptr) if error
41
+ error
42
+ end
43
+
44
+ # @param account[String] should include everything in the db url ahead of 'snowflakecomputing.com'
45
+ # @param port[Integer]
46
+ # @return query_object[Pointer] a pointer to use for subsequent calls not inspectable nor viewable by Ruby
47
+ def connect(
48
+ account = nil,
49
+ warehouse = nil,
50
+ database = nil,
51
+ schema = nil,
52
+ user = nil,
53
+ password = nil,
54
+ role = nil,
55
+ port = nil
56
+ )
57
+ ::Snowflake::Binding.connect(
58
+ account || config.account,
59
+ warehouse || config.warehouse,
60
+ database || config.database,
61
+ schema || config.schema,
62
+ user || config.user,
63
+ password || config.password,
64
+ role || config.role,
65
+ port || config.port
66
+ )
67
+ end
68
+
69
+ # @param db_pointer[Pointer] the pointer which `connect` returned.
70
+ def close(db_pointer)
71
+ ::Snowflake::Binding.close(db_pointer)
72
+ end
73
+
74
+ # @param db_pointer[Pointer] the pointer which `connect` returned.
75
+ # @param statement[String] an executable query which should return number of rows affected
76
+ # @return rowcount[Number] number of rows or nil if there was an error
77
+ def exec(db_pointer, statement)
78
+ count = ::Snowflake::Binding.exec(db_pointer, statement) # returns -1 for error
79
+ count >= 0 ? count : nil
80
+ end
81
+
82
+ # Send a query and then yield each row as an array of strings to the given block
83
+ # @param db_pointer[Pointer] the pointer which `connect` returned.
84
+ # @param query[String] a select query to run.
85
+ # @return error_string
86
+ # @yield List<String>
87
+ def select(db_pointer, sql, field_count: nil)
88
+ return 'db_pointer not initialized' unless db_pointer
89
+ return to_enum(__method__, db_pointer, sql) unless block_given?
90
+
91
+ query_pointer = fetch(db_pointer, sql)
92
+ return last_error if query_pointer.nil? || query_pointer == FFI::Pointer::NULL
93
+
94
+ field_count ||= column_count(query_pointer)
95
+ loop do
96
+ row = get_next_row(query_pointer, field_count)
97
+ return last_error unless row
98
+
99
+ yield row
100
+ end
101
+ nil
102
+ end
103
+
104
+ # @param db_pointer[Pointer] the pointer which `connect` returned.
105
+ # @param query[String] a select query to run.
106
+ # @return query_object[Pointer] a pointer to use for subsequent calls not inspectable nor viewable by Ruby; however,
107
+ # if it's `nil`, check `last_error`
108
+ def fetch(db_pointer, query)
109
+ ::Snowflake::Binding.fetch(db_pointer, query)
110
+ end
111
+
112
+ # @param query_object[Pointer] the pointer which `fetch` returned. Go will gc this object when the query is done; so,
113
+ # don't expect to reference it after the call which returned `nil`
114
+ # @param field_count[Integer] column count: it will seg fault if you provide a number greater than the actual number.
115
+ # Using code should use wrap this in something like
116
+ #
117
+ # @return [List<String>] the column values in order
118
+ def get_next_row(query_object, field_count)
119
+ raw_row = ::Snowflake::Binding.next_row(query_object)
120
+ return nil if raw_row.nil? || raw_row == FFI::Pointer::NULL
121
+
122
+ raw_row.get_array_of_pointer(0, field_count).map do |cstr|
123
+ if cstr == FFI::Pointer::NULL || cstr.nil?
124
+ nil
125
+ else
126
+ str = cstr.read_string
127
+ LibC.free(cstr)
128
+ str
129
+ end
130
+ end
131
+ ensure
132
+ LibC.free(raw_row) if raw_row
133
+ end
134
+
135
+ # @param query_object[Pointer] the pointer which `fetch` returned.
136
+ # @return [List<String>] the column values in order
137
+ def column_names(query_object, field_count = nil)
138
+ raw_row = ::Snowflake::Binding.query_columns(query_object)
139
+ return nil if raw_row.nil? || raw_row == FFI::Pointer::NULL
140
+
141
+ raw_row.get_array_of_pointer(0, field_count).map do |cstr|
142
+ if cstr == FFI::Pointer::NULL || cstr.nil?
143
+ nil
144
+ else
145
+ str = cstr.read_string
146
+ LibC.free(cstr)
147
+ str
148
+ end
149
+ end
150
+ ensure
151
+ LibC.free(raw_row) if raw_row
152
+ end
153
+
154
+ # @param query_object[Pointer] the pointer which `fetch` returned.
155
+ def column_count(query_object)
156
+ ::Snowflake::Binding.query_column_count(query_object)
157
+ end
158
+
159
+ # @return [Dry::Configurable::Config] configuration for the gem.
160
+ def config
161
+ SnowflakeRB::Configuration.config
162
+ end
163
+ end
164
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snowflake_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Darren Cheng
8
+ - Arjun Krishnan
9
+ - Mohammad-Reza
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2023-04-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 1.15.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 1.15.5
29
+ - !ruby/object:Gem::Dependency
30
+ name: dry-configurable
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 1.0.1
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 1.0.1
43
+ description: This gem adds a ruby wrapper around the go native snowflake client
44
+ email:
45
+ - darren@thanx.com
46
+ - arjun@thanx.com
47
+ - mohammad-reza@thanx.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - ext/snowflake-darwin-amd64.so
53
+ - ext/snowflake-darwin-arm64.so
54
+ - ext/snowflake-linux-amd64.so
55
+ - ext/snowflake-linux-arm64.so
56
+ - ext/snowflake.so
57
+ - lib/snowflake_rb.rb
58
+ - lib/snowflake_rb/configuration.rb
59
+ - lib/snowflake_rb/extension.rb
60
+ homepage: https://github.com/thanx/snowflake-rb
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.1.6
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Ruby wrapper for go native snowflake client
83
+ test_files: []