ruby_snowflake_client 1.0.2 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e0c56fd4b0b95f40bbd1ed55dc9fb10bc6284ac198b527b3dfa977f2419fa5f
4
- data.tar.gz: 9351e25573c2553ec2ab6123df8d829e53749ee25e5c6a82de9d4c1e7ca65c4e
3
+ metadata.gz: 75899b8f6ba98da01620d76f6d7077ba73ae8dd7fb1177815300b969d0dbf7b5
4
+ data.tar.gz: 2fdbd266149c69bf17d59c83ab09418ca8f46d23707c5c3d98cc8e6800bf84e2
5
5
  SHA512:
6
- metadata.gz: d414d31259999434fc6323be3fb41a3d1fa854fccad0a09902885bab19e5ed5c68e921d1ca254baaf2cf11f46e91b1fcb9c66f6656f0130798774e5b95f44f48
7
- data.tar.gz: a4e515cdf374ca890ba03ca04602c10f0382bf4768d626d03d50a9febf26f8930bc294a56e8447290bd4677163521c35f9fded4dd81d7741f0d1bb087507c751
6
+ metadata.gz: 180122aebc707aeda9e7cff2fad9ab6361b21d027b4dc23961bb64319ae192338091fff3b5600cbc15a86522c39bff2b4402723c7c0dfbd80570bdea3e3f96cb
7
+ data.tar.gz: 65ab2555c42a044224ee1f5e010f43f7ddc9491205d271bd4459459ee5ffb3f65112bd310742c1bcab797c66e8a590e96f732032142ba3866e2e91afb5feb40b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby_snowflake_client (1.0.2)
4
+ ruby_snowflake_client (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/ext/c-decl.go CHANGED
@@ -29,9 +29,9 @@ func goobj_log(obj unsafe.Pointer) {
29
29
  }
30
30
 
31
31
  //export goobj_retain
32
- func goobj_retain(obj unsafe.Pointer) {
32
+ func goobj_retain(obj unsafe.Pointer, x *C.char) {
33
33
  if LOG_LEVEL > 0 {
34
- fmt.Printf("retain obj %v - currently keeping %d\n", obj, len(objects))
34
+ fmt.Printf("retain obj [%v] %v - currently keeping %d\n", C.GoString(x), obj, len(objects))
35
35
  }
36
36
  objects[obj] = true
37
37
  marked[obj] = 0
data/ext/result.go CHANGED
@@ -11,11 +11,8 @@ VALUE funcall0param(VALUE obj, ID id);
11
11
  import "C"
12
12
 
13
13
  import (
14
- "errors"
15
14
  "fmt"
16
- "io"
17
15
  "math/big"
18
- "strings"
19
16
  "time"
20
17
 
21
18
  gopointer "github.com/mattn/go-pointer"
@@ -28,18 +25,44 @@ func wrapRbRaise(err error) {
28
25
  }
29
26
 
30
27
  func getResultStruct(self C.VALUE) *SnowflakeResult {
31
- ivar := C.rb_ivar_get(self, RESULT_IDENTIFIER)
28
+ return resultMap[self]
29
+ }
32
30
 
33
- str := GetGoStruct(ivar)
34
- ptr := gopointer.Restore(str)
35
- sr, ok := ptr.(*SnowflakeResult)
36
- if !ok || sr.rows == nil {
37
- err := errors.New("Empty result; please run a query via `client.fetch(\"SQL\")`")
38
- wrapRbRaise(err)
39
- return nil
31
+ //export GetRowsNoEnum
32
+ func GetRowsNoEnum(self C.VALUE) C.VALUE {
33
+ res := getResultStruct(self)
34
+ rows := res.rows
35
+
36
+ i := 0
37
+ t1 := time.Now()
38
+ var arr []C.VALUE
39
+
40
+ for rows.Next() {
41
+ if i%5000 == 0 {
42
+ if LOG_LEVEL > 0 {
43
+ fmt.Println("scanning row: ", i)
44
+ }
45
+ }
46
+ x := res.ScanNextRow(false)
47
+ objects[x] = true
48
+ gopointer.Save(x)
49
+ if LOG_LEVEL > 1 {
50
+ // This is VERY noisy
51
+ fmt.Printf("alloced %v\n", &x)
52
+ }
53
+ arr = append(arr, x)
54
+ i = i + 1
55
+ }
56
+ if LOG_LEVEL > 0 {
57
+ fmt.Printf("done with rows.next: %s\n", time.Now().Sub(t1))
40
58
  }
41
59
 
42
- return sr
60
+ rbArr := C.rb_ary_new2(C.long(len(arr)))
61
+ for idx, elem := range arr {
62
+ C.rb_ary_store(rbArr, C.long(idx), elem)
63
+ }
64
+
65
+ return rbArr
43
66
  }
44
67
 
45
68
  //export GetRows
@@ -69,11 +92,6 @@ func GetRows(self C.VALUE) C.VALUE {
69
92
  fmt.Printf("done with rows.next: %s\n", time.Now().Sub(t1))
70
93
  }
71
94
 
72
- //empty for GC
73
- res.rows = nil
74
- res.keptHash = C.Qnil
75
- res.cols = []C.VALUE{}
76
-
77
95
  return self
78
96
  }
79
97
 
@@ -89,10 +107,6 @@ func ObjNextRow(self C.VALUE) C.VALUE {
89
107
  if rows.Next() {
90
108
  r := res.ScanNextRow(false)
91
109
  return r
92
- } else if rows.Err() == io.EOF {
93
- res.rows = nil // free up for gc
94
- res.keptHash = C.Qnil // free up for gc
95
- res.cols = []C.VALUE{}
96
110
  }
97
111
  return C.Qnil
98
112
  }
@@ -104,8 +118,8 @@ func (res SnowflakeResult) ScanNextRow(debug bool) C.VALUE {
104
118
  fmt.Printf("column types: %+v; %+v\n", cts[0], cts[0].ScanType())
105
119
  }
106
120
 
107
- rawResult := make([]any, len(res.cols))
108
- rawData := make([]any, len(res.cols))
121
+ rawResult := make([]any, len(res.columns))
122
+ rawData := make([]any, len(res.columns))
109
123
  for i := range rawResult {
110
124
  rawData[i] = &rawResult[i]
111
125
  }
@@ -117,10 +131,15 @@ func (res SnowflakeResult) ScanNextRow(debug bool) C.VALUE {
117
131
  }
118
132
 
119
133
  // trick from postgres; keep hash: pg_result.c:1088
120
- hash := C.rb_hash_dup(res.keptHash)
134
+ //hash := C.rb_hash_dup(res.keptHash)
135
+ hash := C.rb_hash_new()
136
+ if LOG_LEVEL > 1 {
137
+ // This is very noisy
138
+ fmt.Println("alloc'ed new hash", &hash)
139
+ }
140
+
121
141
  for idx, raw := range rawResult {
122
142
  raw := raw
123
- col_name := res.cols[idx]
124
143
 
125
144
  var rbVal C.VALUE
126
145
 
@@ -151,40 +170,12 @@ func (res SnowflakeResult) ScanNextRow(debug bool) C.VALUE {
151
170
  wrapRbRaise(err)
152
171
  }
153
172
  }
154
- C.rb_hash_aset(hash, col_name, rbVal)
155
- }
156
- return hash
157
- }
158
-
159
- func SafeMakeHash(lenght int, cols []C.VALUE) C.VALUE {
160
- var hash C.VALUE
161
- hash = C.rb_hash_new()
162
-
163
- if LOG_LEVEL > 0 {
164
- fmt.Println("starting make hash")
165
- }
166
- for _, col := range cols {
167
- C.rb_hash_aset(hash, col, C.Qnil)
168
- }
169
- if LOG_LEVEL > 0 {
170
- fmt.Println("end make hash", hash)
173
+ colstr := C.rb_str_new2(C.CString(res.columns[idx]))
174
+ if LOG_LEVEL > 1 {
175
+ // This is very noisy
176
+ fmt.Printf("alloc string: %+v; rubyVal: %+v\n", &colstr, &rbVal)
177
+ }
178
+ C.rb_hash_aset(hash, colstr, rbVal)
171
179
  }
172
180
  return hash
173
181
  }
174
-
175
- func (res *SnowflakeResult) Initialize() {
176
- columns, _ := res.rows.Columns()
177
- rbArr := C.rb_ary_new2(C.long(len(columns)))
178
-
179
- cols := make([]C.VALUE, len(columns))
180
- for idx, colName := range columns {
181
- str := strings.ToLower(colName)
182
- sym := C.rb_str_new2(C.CString(str))
183
- sym = C.rb_str_freeze(sym)
184
- cols[idx] = sym
185
- C.rb_ary_store(rbArr, C.long(idx), sym)
186
- }
187
-
188
- res.cols = cols
189
- res.keptHash = SafeMakeHash(len(columns), cols)
190
- }
@@ -8,8 +8,9 @@ VALUE ObjFetch(VALUE,VALUE);
8
8
  VALUE ObjNextRow(VALUE);
9
9
  VALUE Inspect(VALUE);
10
10
  VALUE GetRows(VALUE);
11
+ VALUE GetRowsNoEnum(VALUE);
11
12
 
12
- VALUE NewGoStruct(VALUE klass, void *p);
13
+ VALUE NewGoStruct(VALUE klass, char* reason, void *p);
13
14
  VALUE GoRetEnum(VALUE,int,VALUE);
14
15
  void* GetGoStruct(VALUE obj);
15
16
  void RbGcGuard(VALUE ptr);
@@ -21,18 +22,18 @@ import "C"
21
22
  import (
22
23
  "context"
23
24
  "database/sql"
24
- "errors"
25
25
  "fmt"
26
+ "strings"
26
27
  "time"
27
28
 
28
- gopointer "github.com/mattn/go-pointer"
29
29
  sf "github.com/snowflakedb/gosnowflake"
30
30
  )
31
31
 
32
32
  type SnowflakeResult struct {
33
- rows *sql.Rows
34
- keptHash C.VALUE
35
- cols []C.VALUE
33
+ rows *sql.Rows
34
+ //keptHash C.VALUE
35
+ columns []string
36
+ //cols []C.VALUE
36
37
  }
37
38
  type SnowflakeClient struct {
38
39
  db *sql.DB
@@ -42,12 +43,13 @@ var rbSnowflakeClientClass C.VALUE
42
43
  var rbSnowflakeResultClass C.VALUE
43
44
  var rbSnowflakeModule C.VALUE
44
45
 
45
- var DB_IDENTIFIER = C.rb_intern(C.CString("db"))
46
46
  var RESULT_IDENTIFIER = C.rb_intern(C.CString("rows"))
47
47
  var RESULT_DURATION = C.rb_intern(C.CString("@query_duration"))
48
48
  var ERROR_IDENT = C.rb_intern(C.CString("@error"))
49
49
 
50
50
  var objects = make(map[interface{}]bool)
51
+ var resultMap = make(map[C.VALUE]*SnowflakeResult)
52
+ var clientRef = make(map[C.VALUE]*SnowflakeClient)
51
53
 
52
54
  var LOG_LEVEL = 0
53
55
  var empty C.VALUE = C.Qnil
@@ -78,13 +80,7 @@ func Connect(self C.VALUE, account C.VALUE, warehouse C.VALUE, database C.VALUE,
78
80
  C.rb_ivar_set(self, ERROR_IDENT, RbString(errStr))
79
81
  }
80
82
  rs := SnowflakeClient{db}
81
- ptr := gopointer.Save(&rs)
82
- rbStruct := C.NewGoStruct(
83
- rbSnowflakeClientClass,
84
- ptr,
85
- )
86
-
87
- C.rb_ivar_set(self, DB_IDENTIFIER, rbStruct)
83
+ clientRef[self] = &rs
88
84
  }
89
85
 
90
86
  func (x SnowflakeClient) Fetch(statement C.VALUE) C.VALUE {
@@ -113,46 +109,28 @@ func (x SnowflakeClient) Fetch(statement C.VALUE) C.VALUE {
113
109
  }
114
110
 
115
111
  result := C.rb_class_new_instance(0, &empty, rbSnowflakeResultClass)
116
- rs := SnowflakeResult{rows, C.Qnil, []C.VALUE{}}
117
- rs.Initialize()
118
- ptr := gopointer.Save(&rs)
119
- rbStruct := C.NewGoStruct(
120
- rbSnowflakeClientClass,
121
- ptr,
122
- )
123
- C.RbGcGuard(rbStruct)
124
- C.RbGcGuard(rbSnowflakeResultClass)
125
- C.rb_ivar_set(result, RESULT_IDENTIFIER, rbStruct)
112
+ cols, _ := rows.Columns()
113
+ for idx, col := range cols {
114
+ col := col
115
+ cols[idx] = strings.ToLower(col)
116
+ }
117
+ rs := SnowflakeResult{rows, cols}
118
+ resultMap[result] = &rs
126
119
  C.rb_ivar_set(result, RESULT_DURATION, RbNumFromDouble(C.double(duration)))
127
120
  return result
128
121
  }
129
122
 
130
123
  //export ObjFetch
131
124
  func ObjFetch(self C.VALUE, statement C.VALUE) C.VALUE {
132
- var q C.VALUE
133
- q = C.rb_ivar_get(self, DB_IDENTIFIER)
134
-
135
- req := C.GetGoStruct(q)
136
- f := gopointer.Restore(req)
137
- x, ok := f.(*SnowflakeClient)
138
- if !ok {
139
- wrapRbRaise((errors.New("cannot convert SnowflakeClient pointer in ObjFetch")))
140
- }
125
+ x, _ := clientRef[self]
141
126
 
142
127
  return x.Fetch(statement)
143
128
  }
144
129
 
145
130
  //export Inspect
146
131
  func Inspect(self C.VALUE) C.VALUE {
147
- q := C.rb_ivar_get(self, DB_IDENTIFIER)
148
- if q == C.Qnil {
149
- return RbString("Object is not instantiated")
150
- }
151
-
152
- req := C.GetGoStruct(q)
153
- f := gopointer.Restore(req)
154
- x := f.(*SnowflakeClient)
155
- return RbString(fmt.Sprintf("%+v", x))
132
+ x := clientRef[self]
133
+ return RbString(fmt.Sprintf("Snowflake::Client <%+v>", x))
156
134
  }
157
135
 
158
136
  //export Init_ruby_snowflake_client_ext
@@ -161,10 +139,20 @@ func Init_ruby_snowflake_client_ext() {
161
139
  rbSnowflakeClientClass = C.rb_define_class_under(rbSnowflakeModule, C.CString("Client"), C.rb_cObject)
162
140
  rbSnowflakeResultClass = C.rb_define_class_under(rbSnowflakeModule, C.CString("Result"), C.rb_cObject)
163
141
 
142
+ objects[rbSnowflakeResultClass] = true
143
+ objects[rbSnowflakeClientClass] = true
144
+ objects[rbSnowflakeModule] = true
145
+ objects[RESULT_DURATION] = true
146
+ objects[ERROR_IDENT] = true
147
+ C.RbGcGuard(RESULT_DURATION)
148
+ //C.RbGcGuard(RESULT_IDENTIFIER)
149
+ C.RbGcGuard(ERROR_IDENT)
150
+
164
151
  C.rb_define_method(rbSnowflakeResultClass, C.CString("next_row"), (*[0]byte)(C.ObjNextRow), 0)
165
152
  // `get_rows` is private as this can lead to SEGFAULT errors if not invoked
166
153
  // with GC.disable due to undetermined issues caused by the Ruby GC.
167
154
  C.rb_define_private_method(rbSnowflakeResultClass, C.CString("_get_rows"), (*[0]byte)(C.GetRows), 0)
155
+ C.rb_define_method(rbSnowflakeResultClass, C.CString("get_rows_no_enum"), (*[0]byte)(C.GetRowsNoEnum), 0)
168
156
 
169
157
  C.rb_define_private_method(rbSnowflakeClientClass, C.CString("_connect"), (*[0]byte)(C.Connect), 7)
170
158
  C.rb_define_method(rbSnowflakeClientClass, C.CString("inspect"), (*[0]byte)(C.Inspect), 0)
data/ext/wrapper.go CHANGED
@@ -24,7 +24,7 @@ VALUE RbNumFromLong(long v) {
24
24
  return LONG2NUM(v);
25
25
  }
26
26
 
27
- void goobj_retain(void *);
27
+ void goobj_retain(void *, char*);
28
28
  void goobj_free(void *);
29
29
  void goobj_log(void *);
30
30
  void goobj_mark(void *);
@@ -42,9 +42,9 @@ static const rb_data_type_t go_type = {
42
42
  };
43
43
 
44
44
  VALUE
45
- NewGoStruct(VALUE klass, void *p)
45
+ NewGoStruct(VALUE klass, char* reason, void *p)
46
46
  {
47
- goobj_retain(p);
47
+ goobj_retain(p, reason);
48
48
  return TypedData_Wrap_Struct(klass, &go_type, p);
49
49
  }
50
50
 
@@ -125,7 +125,8 @@ func RbString(str string) C.VALUE {
125
125
  if len(str) == 0 {
126
126
  return C.rb_utf8_str_new(nil, C.long(0))
127
127
  }
128
- cstr := (*C.char)(unsafe.Pointer(&(*(*[]byte)(unsafe.Pointer(&str)))[0]))
128
+ //cstr := (*C.char)(unsafe.Pointer(&(*(*[]byte)(unsafe.Pointer(&str)))[0]))
129
+ cstr := C.CString(str)
129
130
  return C.rb_utf8_str_new(cstr, C.long(len(str)))
130
131
  }
131
132
 
@@ -1,3 +1,3 @@
1
1
  module RubySnowflakeClient
2
- VERSION = '1.0.2'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Snowflake
4
4
  require "ruby_snowflake_client_ext" # build bundle of the go files
5
+ LOG_LEVEL = 0
5
6
 
6
7
  class Error < StandardError
7
8
  attr_reader :details
@@ -51,13 +52,24 @@ module Snowflake
51
52
  def get_all_rows(&blk)
52
53
  GC.disable
53
54
  if blk
54
- _get_rows(&blk)
55
+ while r = next_row do
56
+ yield r
57
+ end
55
58
  else
56
- _get_rows.to_a
59
+ get_rows_array
57
60
  end
58
61
  ensure
59
62
  GC.enable
60
- GC.start
61
63
  end
64
+
65
+ private
66
+ def get_rows_array
67
+ arr = []
68
+ while r = next_row do
69
+ puts "at #{arr.length}" if arr.length % 15000 == 0 && LOG_LEVEL > 0
70
+ arr << r
71
+ end
72
+ arr
73
+ end
62
74
  end
63
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_snowflake_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rinsed
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-08 00:00:00.000000000 Z
11
+ date: 2023-06-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Using the `Go` library for Snowflake to query and creating native Ruby objects,