snowflake_rb 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []