activerecord-pinot-adapter 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34d05b55c0db37ef45a48eb13874ea21fd328e0d68fbdf1cfda03371401ff123
4
- data.tar.gz: 0732be5d8c9cfc27ab00017d8daee56e07b1e8924996919bd8e673553cc8a635
3
+ metadata.gz: c09ea0dd800fdfd76f57fae011fbe90ef3e42498ea51b58d86cfcd0b22611a89
4
+ data.tar.gz: e336a92e3d15be236bfbfea8cb775be28f9d8b459e8ff04a92fd92071d9915a5
5
5
  SHA512:
6
- metadata.gz: 88cbf5c67cc7dcdf077aaa5ab65b9da04926861acbc588793017f07b06b7cf316fc6984f59e83b36f17ab16543a1521efaa73fa390af85b566ec49641962f2db
7
- data.tar.gz: 104d88ad0b25397026ab01f5a8d6a9f4aa2373adde0de49ba5166b60eeeaccb3605ccfa0b7a1f86ae92a25137c03c58aa85d2cba480a8e3b9790ea8575105e2c
6
+ metadata.gz: 33d9d6d982ec4b36813addc7a4e9463cc1279befb2e1692d7a66aa634e306bb5d7ad7a330c71af8ca3cbb542caf689a02ad4a581b6905f3d4da8a8d36caf7cd5
7
+ data.tar.gz: 63a9ab22309ef70b77a9ef72d319a1311f288c1adb62f72821e2f258bf5465695e228d6e8589bcfcca51479914c415f43c73d451e5f31ccc4510c10591acb115
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ # with Minitest::Unit
20
+ watch(%r{^test/(.*)/?test_(.*)\.rb$})
21
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
+ watch(%r{^test/test_helper\.rb$}) { "test" }
23
+
24
+ # with Minitest::Spec
25
+ # watch(%r{^spec/(.*)_spec\.rb$})
26
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
+ # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
28
+
29
+ # Rails 4
30
+ # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
31
+ # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
32
+ # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
33
+ # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
34
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
35
+ # watch(%r{^test/.+_test\.rb$})
36
+ # watch(%r{^test/test_helper\.rb$}) { 'test' }
37
+
38
+ # Rails < 4
39
+ # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
40
+ # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
41
+ # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
42
+ end
@@ -0,0 +1,43 @@
1
+ version: '3.7'
2
+ services:
3
+ pinot-zookeeper:
4
+ image: zookeeper:3.5.6
5
+ container_name: pinot-zookeeper
6
+ ports:
7
+ - "2181:2181"
8
+ environment:
9
+ ZOOKEEPER_CLIENT_PORT: 2181
10
+ ZOOKEEPER_TICK_TIME: 2000
11
+ pinot-controller:
12
+ image: apachepinot/pinot:1.0.0
13
+ command: "StartController -zkAddress pinot-zookeeper:2181"
14
+ container_name: pinot-controller
15
+ restart: unless-stopped
16
+ ports:
17
+ - "9000:9000"
18
+ environment:
19
+ JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms1G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-controller.log"
20
+ depends_on:
21
+ - pinot-zookeeper
22
+ pinot-broker:
23
+ image: apachepinot/pinot:1.0.0
24
+ command: "StartBroker -zkAddress pinot-zookeeper:2181"
25
+ restart: unless-stopped
26
+ container_name: "pinot-broker"
27
+ ports:
28
+ - "8099:8099"
29
+ environment:
30
+ JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-broker.log"
31
+ depends_on:
32
+ - pinot-controller
33
+ pinot-server:
34
+ image: apachepinot/pinot:1.0.0
35
+ command: "StartServer -zkAddress pinot-zookeeper:2181"
36
+ restart: unless-stopped
37
+ container_name: "pinot-server"
38
+ ports:
39
+ - "8098:8098"
40
+ environment:
41
+ JAVA_OPTS: "-Dplugins.dir=/opt/pinot/plugins -Xms4G -Xmx16G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:gc-pinot-server.log"
42
+ depends_on:
43
+ - pinot-broker
@@ -0,0 +1,53 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PinotAdapter < AbstractAdapter
4
+ module TableStructure
5
+ def self.from_schema(schema)
6
+ fields = []
7
+ schema["dimensionFieldSpecs"].each do |f|
8
+ fields << {
9
+ "name" => f["name"],
10
+ "type" => f["dataType"],
11
+ "pinot_type" => "dimension"
12
+ }
13
+ end
14
+ schema["metricFieldSpecs"].each do |f|
15
+ fields << {
16
+ "name" => f["name"],
17
+ "type" => f["dataType"],
18
+ "pinot_type" => "metric"
19
+ }
20
+ end
21
+ schema["dateTimeFieldSpecs"].each do |f|
22
+ fields << {
23
+ "name" => f.delete("name"),
24
+ "type" => f.delete("dataType"),
25
+ "pinot_type" => "dateTime",
26
+ "metadata" => f
27
+ }
28
+ end
29
+ # normalize values
30
+ fields.each do |f|
31
+ f["type"] = normalize_type(f["type"])
32
+ end
33
+ # set primary keys
34
+ primary_key_columns = schema["primaryKeyColumns"] || []
35
+ primary_key_columns.each do |pk|
36
+ pk_fields = fields.select { |x| x["name"] == pk }
37
+ pk_fields.each { |field| field["pk"] = 1 }
38
+ end
39
+ fields
40
+ end
41
+
42
+ def self.normalize_type(type)
43
+ case type
44
+ when "INT" then "integer"
45
+ when "STRING" then "varchar"
46
+ else
47
+ type
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,129 @@
1
+ require_relative "pinot_adapter/table_structure"
2
+
3
+ module ActiveRecord
4
+ module ConnectionHandling # :nodoc:
5
+ def pinot_adapter_class
6
+ ConnectionAdapters::PinotAdapter
7
+ end
8
+
9
+ def pinot_connection(config)
10
+ pinot_adapter_class.new(config)
11
+ end
12
+ end
13
+
14
+ module ConnectionAdapters
15
+ class PinotAdapter < AbstractAdapter
16
+ TYPES = {
17
+ "INT" => Type::Integer.new,
18
+ "TIMESTAMP" => Type::DateTime.new,
19
+ "FLOAT" => Type::Decimal.new,
20
+ "LONG" => Type::Decimal.new
21
+ }
22
+ def initialize(config = {})
23
+ @pinot_host = config.fetch(:host)
24
+ @pinot_port = config.fetch(:port)
25
+ @pinot_controller_port = config.fetch(:controller_port)
26
+ @pinot_controller_host = config.fetch(:controller_host) || @pinot_host
27
+ # TODO: does it need connection pooling?
28
+ @pinot_client = ::Pinot::Client.new(host: @pinot_host, port: @pinot_port, controller_host: @pinot_controller_host, controller_port: @pinot_controller_port)
29
+
30
+ super(config)
31
+ end
32
+
33
+ def default_prepared_statements
34
+ false
35
+ end
36
+
37
+ def table_structure(table_name)
38
+ schema = @pinot_client.schema(table_name)
39
+ @table_structure = TableStructure.from_schema(schema)
40
+ @table_structure.sort_by! { |x| x[:name] }
41
+ end
42
+
43
+ def new_column_from_field(table_name, field, definitions)
44
+ default = nil
45
+
46
+ type_metadata = fetch_type_metadata(field["type"])
47
+ default_value = extract_value_from_default(default)
48
+ default_function = extract_default_function(default_value, default)
49
+
50
+ Column.new(
51
+ field["name"],
52
+ default_value,
53
+ type_metadata,
54
+ field["notnull"].to_i == 0,
55
+ default_function,
56
+ collation: field["collation"]
57
+ )
58
+ end
59
+ alias_method :column_definitions, :table_structure
60
+
61
+ def extract_value_from_default(default)
62
+ case default
63
+ when /^null$/i
64
+ nil
65
+ # Quoted types
66
+ when /^'([^|]*)'$/m
67
+ $1.gsub("''", "'")
68
+ # Quoted types
69
+ when /^"([^|]*)"$/m
70
+ $1.gsub('""', '"')
71
+ # Numeric types
72
+ when /\A-?\d+(\.\d*)?\z/
73
+ $&
74
+ # Binary columns
75
+ when /x'(.*)'/
76
+ [$1].pack("H*")
77
+ else
78
+ # Anything else is blank or some function
79
+ # and we can't know the value of that, so return nil.
80
+ nil
81
+ end
82
+ end
83
+
84
+ def extract_default_function(default_value, default)
85
+ default if has_default_function?(default_value, default)
86
+ end
87
+
88
+ def has_default_function?(default_value, default)
89
+ !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
90
+ end
91
+
92
+ INTEGER_REGEX = /integer/i
93
+ def is_column_the_rowid?(field, column_definitions)
94
+ return false unless INTEGER_REGEX.match?(field["type"]) && field["pk"] == 1
95
+ # is the primary key a single column?
96
+ column_definitions.one? { |c|
97
+ col_pk = c["pk"] || 0
98
+ col_pk > 0
99
+ }
100
+ end
101
+
102
+ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
103
+ # rows = [
104
+ # [Time.now, 1, 2.0],
105
+ # [Time.now, 2, 2.0],
106
+ # [Time.now, 2, 2.0]
107
+ # ]
108
+ response = @pinot_client.execute(sql)
109
+ rows = response.rows
110
+ columns = response.columns
111
+ columns.transform_values! { |value| TYPES.fetch(value, value) }
112
+ ActiveRecord::Result.new(
113
+ response.columns.keys,
114
+ rows.to_a,
115
+ columns
116
+ )
117
+ end
118
+
119
+ def data_source_sql(name = nil, type: nil)
120
+ end
121
+
122
+ def primary_keys(table_name)
123
+ []
124
+ end
125
+ end
126
+
127
+ ActiveSupport.run_load_hooks(:active_record_pinotadapter, PinotAdapter)
128
+ end
129
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pinot"
4
+ require "active_record"
5
+ require_relative "../connection_adapters/pinot_adapter"
6
+
7
+ module ActiveRecord
8
+ module Pinot
9
+ module Adapter
10
+ class Error < StandardError; end
11
+ # Your code goes here...
12
+ end
13
+ end
14
+ end
@@ -3,7 +3,7 @@
3
3
  module Activerecord
4
4
  module Pinot
5
5
  module Adapter
6
- VERSION = "0.1.0"
6
+ VERSION = "0.1.1"
7
7
  end
8
8
  end
9
9
  end
@@ -1,12 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "adapter/version"
4
-
5
- module Activerecord
6
- module Pinot
7
- module Adapter
8
- class Error < StandardError; end
9
- # Your code goes here...
10
- end
11
- end
12
- end
4
+ require "pinot"
5
+ require "active_record"
6
+ require "active_record/pinot/adapter"
metadata CHANGED
@@ -1,15 +1,105 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-pinot-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Celso Fernandes
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-16 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-05-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 5.2.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: pinot
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: guard-minitest
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: minitest-reporters
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: minitest-focus
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
13
103
  description: ActiveRecord Apache Pinot Adapter
14
104
  email:
15
105
  - celso.fernandes@clickfunnels.com
@@ -20,9 +110,13 @@ files:
20
110
  - ".standard.yml"
21
111
  - CHANGELOG.md
22
112
  - CODE_OF_CONDUCT.md
113
+ - Guardfile
23
114
  - README.md
24
115
  - Rakefile
25
- - activerecord-pinot-adapter.gemspec
116
+ - docker-compose.yml
117
+ - lib/active_record/connection_adapters/pinot_adapter.rb
118
+ - lib/active_record/connection_adapters/pinot_adapter/table_structure.rb
119
+ - lib/active_record/pinot/adapter.rb
26
120
  - lib/activerecord/pinot/adapter.rb
27
121
  - lib/activerecord/pinot/adapter/version.rb
28
122
  - sig/activerecord/pinot/adapter.rbs
@@ -47,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
141
  - !ruby/object:Gem::Version
48
142
  version: '0'
49
143
  requirements: []
50
- rubygems_version: 3.4.21
144
+ rubygems_version: 3.5.3
51
145
  signing_key:
52
146
  specification_version: 4
53
147
  summary: ActiveRecord Apache Pinot Adapter
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/activerecord/pinot/adapter/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "activerecord-pinot-adapter"
7
- spec.version = Activerecord::Pinot::Adapter::VERSION
8
- spec.authors = ["Celso Fernandes"]
9
- spec.email = ["celso.fernandes@clickfunnels.com"]
10
-
11
- spec.summary = "ActiveRecord Apache Pinot Adapter"
12
- spec.description = "ActiveRecord Apache Pinot Adapter"
13
- spec.homepage = "https://github.com/fernandes/activerecord-pinot-adapter"
14
- spec.required_ruby_version = ">= 2.6.0"
15
-
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/fernandes/activerecord-pinot-adapter"
18
- spec.metadata["changelog_uri"] = "https://github.com/fernandes/activerecord-pinot-adapter/blob/main/CHANGELOG.md"
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(__dir__) do
23
- `git ls-files -z`.split("\x0").reject do |f|
24
- (File.expand_path(f) == __FILE__) ||
25
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
26
- end
27
- end
28
- spec.bindir = "exe"
29
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
-
32
- # Uncomment to register a new dependency of your gem
33
- # spec.add_dependency "example-gem", "~> 1.0"
34
-
35
- # For more information and examples about making a new gem, check out our
36
- # guide at: https://bundler.io/guides/creating_gem.html
37
- end