quill-sql 0.1.2 → 0.1.4

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: 3fd0fbadace7b0e612933cadd0a2a27e0a2579f44b2de6d604d6cc50b481d10b
4
- data.tar.gz: f93961c1903a18b498105e99ac17fc36f4c77ded0db2a2076cd0d15973d30d9e
3
+ metadata.gz: 76485b20728b04f2100bcf4d6a35b4308fd7c14be60f9ffb992a012e97b083c2
4
+ data.tar.gz: 3fb5e7d7acda9a4011c7e23f9a778eb65d4dc020418b9d13259ca0cfaef69cb6
5
5
  SHA512:
6
- metadata.gz: 4d5fb6dff9e6f7b062ced96950e56073e23f51314e0d7c3c6a855ce2a1f3f159436306b14cb2b6989f869dc1b474d602d0235fe9eed449068fc73589c1c07559
7
- data.tar.gz: 6d3242c396458360bea230008cf71aedc05c50aaf4354b6c53b41a723643607edf1a6da4c6ef12169df4aa11b1023fde312d756ed3a508d18982789174419ef7
6
+ metadata.gz: e92f9001384b9c4313c6d21f0b41a2a37f14b30542775d994c591c05196b49cdf57f219370e6d58d72bbb3959442c3e7715b89c297b09073b1c58c68ebeecf53
7
+ data.tar.gz: b7cea1082503f99ecb4f5ae2144b66d9706fa2994fd33e6bf442a67145c748bcc9866c67cd8b698405bf1d1539affd0e51ecdafcfabea7705c921d696488ed3d
data/lib/db/clickhouse.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'uri'
2
2
  require 'json'
3
3
  require 'click_house'
4
+ require 'active_support/inflector'
4
5
 
5
6
  module ClickHouseHelper
6
7
  # Constants
@@ -242,7 +243,7 @@ module ClickHouseHelper
242
243
  end
243
244
 
244
245
  def get_foreign_keys_clickhouse(client, schema_name, table_name, primary_key)
245
- depluralized_table_name = depluralize(table_name)
246
+ depluralized_table_name = table_name.singularize
246
247
 
247
248
  sql = <<~SQL
248
249
  SELECT column_name
@@ -251,7 +252,7 @@ module ClickHouseHelper
251
252
  AND table_name != '#{table_name}'
252
253
  AND (column_name = '#{primary_key}'
253
254
  OR column_name = '#{depluralized_table_name}_#{primary_key}'
254
- OR column_name = '#{depluralized_table_name}#{capitalize(primary_key)}')
255
+ OR column_name = '#{depluralized_table_name}#{primary_key.capitalize}')
255
256
  SQL
256
257
 
257
258
  results = run_query_clickhouse(sql, client)
@@ -270,7 +271,7 @@ module ClickHouseHelper
270
271
  OR column_name LIKE '%\_id'
271
272
  OR column_name LIKE '%Id'
272
273
  OR column_name LIKE '%\_#{primary_key}'
273
- OR column_name LIKE '%#{capitalize(primary_key)}')
274
+ OR column_name LIKE '%#{primary_key.capitalize}')
274
275
  SQL
275
276
 
276
277
  results = run_query_clickhouse(sql, client)
@@ -317,17 +318,5 @@ module ClickHouseHelper
317
318
  password: parsed.password || ''
318
319
  }
319
320
  end
320
-
321
- private
322
-
323
- def capitalize(str)
324
- str.capitalize
325
- end
326
-
327
- def depluralize(str)
328
- # Simple depluralization - you might want to use a proper library like ActiveSupport
329
- return str[0..-2] if str.end_with?('s')
330
- str
331
- end
332
321
  end
333
322
  end
data/lib/db/db_helper.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  require 'json'
2
2
  require 'uri'
3
3
  require_relative 'clickhouse'
4
+ require_relative 'postgres'
4
5
 
5
6
  module DatabaseHelper
6
- SUPPORTED_DATABASES = ['clickhouse'].freeze # Add others as they're implemented
7
+ SUPPORTED_DATABASES = ['clickhouse', 'postgresql'].freeze
7
8
 
8
9
  class QuillQueryResults
9
10
  attr_reader :fields, :rows
@@ -20,6 +21,8 @@ module DatabaseHelper
20
21
  case database_type.downcase
21
22
  when 'clickhouse'
22
23
  ClickHouseHelper.format_clickhouse_config(connection_string)
24
+ when 'postgresql'
25
+ PostgresHelper.format_postgres_config(connection_string)
23
26
  else
24
27
  raise DatabaseError, "Invalid database type: #{database_type}"
25
28
  end
@@ -29,6 +32,8 @@ module DatabaseHelper
29
32
  case database_type.downcase
30
33
  when 'clickhouse'
31
34
  ClickHouseHelper.connect_to_clickhouse(config)
35
+ when 'postgresql'
36
+ PostgresHelper.connect_to_postgres(config)
32
37
  else
33
38
  raise DatabaseError, "Invalid database type: #{database_type}"
34
39
  end
@@ -50,6 +55,8 @@ module DatabaseHelper
50
55
  case database_type.downcase
51
56
  when 'clickhouse'
52
57
  ClickHouseHelper.run_query_clickhouse(sql, connection)
58
+ when 'postgresql'
59
+ PostgresHelper.run_query_postgres(sql, connection)
53
60
  else
54
61
  raise DatabaseError, "Invalid database type: #{database_type}"
55
62
  end
@@ -65,6 +72,8 @@ module DatabaseHelper
65
72
  case database_type.downcase
66
73
  when 'clickhouse'
67
74
  ClickHouseHelper.disconnect_from_clickhouse(database)
75
+ when 'postgresql'
76
+ PostgresHelper.disconnect_from_postgres(database)
68
77
  end
69
78
  end
70
79
 
@@ -72,6 +81,8 @@ module DatabaseHelper
72
81
  case database_type.downcase
73
82
  when 'clickhouse'
74
83
  ClickHouseHelper.get_schemas_clickhouse(connection)
84
+ when 'postgresql'
85
+ PostgresHelper.get_schemas_postgres(connection)
75
86
  else
76
87
  raise DatabaseError, "Invalid database type: #{database_type}"
77
88
  end
@@ -81,6 +92,8 @@ module DatabaseHelper
81
92
  case database_type.downcase
82
93
  when 'clickhouse'
83
94
  ClickHouseHelper.get_tables_by_schema_clickhouse(connection, schema_name)
95
+ when 'postgresql'
96
+ PostgresHelper.get_tables_by_schema_postgres(connection, schema_name)
84
97
  else
85
98
  raise DatabaseError, "Invalid database type: #{database_type}"
86
99
  end
@@ -90,6 +103,8 @@ module DatabaseHelper
90
103
  case database_type.downcase
91
104
  when 'clickhouse'
92
105
  ClickHouseHelper.get_columns_by_table_clickhouse(connection, schema_name, table_name)
106
+ when 'postgresql'
107
+ PostgresHelper.get_columns_by_table_postgres(connection, schema_name, table_name)
93
108
  else
94
109
  raise DatabaseError, "Invalid database type: #{database_type}"
95
110
  end
@@ -99,6 +114,8 @@ module DatabaseHelper
99
114
  case database_type.downcase
100
115
  when 'clickhouse'
101
116
  ClickHouseHelper.get_foreign_keys_clickhouse(connection, schema_name, table_name, primary_key)
117
+ when 'postgresql'
118
+ PostgresHelper.get_foreign_keys_postgres(connection, schema_name, table_name, primary_key)
102
119
  else
103
120
  raise DatabaseError, "Invalid database type: #{database_type}"
104
121
  end
@@ -108,6 +125,8 @@ module DatabaseHelper
108
125
  case database_type.downcase
109
126
  when 'clickhouse'
110
127
  ClickHouseHelper.get_schema_column_info_clickhouse(connection, schema_name, tables)
128
+ when 'postgresql'
129
+ PostgresHelper.get_schema_column_info_postgres(connection, schema_name, tables)
111
130
  else
112
131
  raise DatabaseError, "Invalid database type: #{database_type}"
113
132
  end
@@ -0,0 +1,177 @@
1
+ require 'pg'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'active_support/inflector'
5
+
6
+ module PostgresHelper
7
+ class << self
8
+ def connect_to_postgres(config)
9
+ conn = PG.connect(
10
+ host: config[:host],
11
+ port: config[:port],
12
+ dbname: config[:database],
13
+ user: config[:username],
14
+ password: config[:password],
15
+ sslmode: config[:sslmode] || 'prefer',
16
+ gssencmode: 'disable', # Disable GSSAPI encryption
17
+ krbsrvname: nil, # Disable Kerberos service name
18
+ target_session_attrs: 'read-write'
19
+ )
20
+ conn
21
+ end
22
+
23
+ def disconnect_from_postgres(client)
24
+ client.close if client.respond_to?(:close)
25
+ end
26
+
27
+ def run_query_postgres(sql, client)
28
+ result = client.exec(sql)
29
+
30
+ fields = result.fields.map do |field|
31
+ {
32
+ name: field,
33
+ dataTypeID: result.ftype(result.fields.index(field))
34
+ }
35
+ end
36
+
37
+ {
38
+ fields: fields,
39
+ rows: result.values.map { |row| result.fields.zip(row).to_h }
40
+ }
41
+ end
42
+
43
+ def get_schemas_postgres(client)
44
+ sql = <<~SQL
45
+ SELECT schema_name
46
+ FROM information_schema.schemata
47
+ WHERE schema_name NOT LIKE 'pg_%'
48
+ AND schema_name != 'information_schema'
49
+ SQL
50
+
51
+ results = run_query_postgres(sql, client)
52
+ results[:rows].map { |row| row['schema_name'] }
53
+ end
54
+
55
+ def get_tables_by_schema_postgres(client, schema_names)
56
+ all_tables = schema_names.flat_map do |schema|
57
+ sql = <<~SQL
58
+ SELECT table_name, table_schema
59
+ FROM information_schema.tables
60
+ WHERE table_schema = '#{schema}'
61
+ AND table_type = 'BASE TABLE'
62
+ SQL
63
+
64
+ results = run_query_postgres(sql, client)
65
+ results[:rows].map do |row|
66
+ {
67
+ tableName: row['table_name'],
68
+ schemaName: row['table_schema']
69
+ }
70
+ end
71
+ end
72
+
73
+ all_tables
74
+ end
75
+
76
+ def get_columns_by_table_postgres(client, schema_name, table_name)
77
+ sql = <<~SQL
78
+ SELECT column_name
79
+ FROM information_schema.columns
80
+ WHERE table_schema = '#{schema_name}'
81
+ AND table_name = '#{table_name}'
82
+ ORDER BY ordinal_position
83
+ SQL
84
+
85
+ results = run_query_postgres(sql, client)
86
+ results[:rows].map { |row| row['column_name'] }
87
+ end
88
+
89
+ def get_foreign_keys_postgres(client, schema_name, table_name, primary_key)
90
+ depluralized_table_name = table_name.singularize
91
+
92
+ sql = <<~SQL
93
+ SELECT column_name
94
+ FROM information_schema.columns
95
+ WHERE table_schema = '#{schema_name}'
96
+ AND table_name != '#{table_name}'
97
+ AND (column_name = '#{primary_key}'
98
+ OR column_name = '#{depluralized_table_name}_#{primary_key}'
99
+ OR column_name = '#{depluralized_table_name}#{primary_key.capitalize}')
100
+ SQL
101
+
102
+ results = run_query_postgres(sql, client)
103
+ foreign_keys = results[:rows].map { |key| key['column_name'] }
104
+
105
+ foreign_keys = foreign_keys.reject { |key| ['id', '_id_'].include?(key) }
106
+ foreign_keys = foreign_keys.uniq
107
+
108
+ if foreign_keys.empty?
109
+ sql = <<~SQL
110
+ SELECT column_name
111
+ FROM information_schema.columns
112
+ WHERE table_schema = '#{schema_name}'
113
+ AND table_name != '#{table_name}'
114
+ AND (column_name LIKE '#{table_name}%'
115
+ OR column_name LIKE '%\\_id'
116
+ OR column_name LIKE '%Id'
117
+ OR column_name LIKE '%\\_#{primary_key}'
118
+ OR column_name LIKE '%#{primary_key.capitalize}')
119
+ SQL
120
+
121
+ results = run_query_postgres(sql, client)
122
+ foreign_keys = results[:rows].map { |key| key['column_name'] }.uniq
123
+ end
124
+
125
+ foreign_keys
126
+ end
127
+
128
+ def get_schema_column_info_postgres(client, schema_name, table_names)
129
+ table_names.map do |table_name|
130
+ query = <<~SQL
131
+ SELECT
132
+ column_name,
133
+ udt_name as field_type,
134
+ ordinal_position as sort_number
135
+ FROM information_schema.columns
136
+ WHERE table_schema = '#{table_name[:schemaName]}'
137
+ AND table_name = '#{table_name[:tableName]}'
138
+ ORDER BY sort_number
139
+ SQL
140
+
141
+ results = run_query_postgres(query, client)
142
+ {
143
+ tableName: "#{table_name[:schemaName]}.#{table_name[:tableName]}",
144
+ displayName: "#{table_name[:schemaName]}.#{table_name[:tableName]}",
145
+ columns: results[:rows].map do |row|
146
+ {
147
+ columnName: row['column_name'],
148
+ displayName: row['column_name'],
149
+ dataTypeID: get_pg_type_oid(row['field_type']),
150
+ fieldType: row['field_type']
151
+ }
152
+ end
153
+ }
154
+ end
155
+ end
156
+
157
+ def format_postgres_config(connection_string)
158
+ parsed = URI.parse(connection_string)
159
+
160
+ {
161
+ host: parsed.host,
162
+ port: parsed.port || 5432,
163
+ database: parsed.path[1..-1],
164
+ username: parsed.user || 'postgres',
165
+ password: parsed.password || '',
166
+ sslmode: parsed.query&.split('&')&.find { |param| param.start_with?('sslmode=') }&.split('=')&.last || 'prefer'
167
+ }
168
+ end
169
+
170
+ private
171
+
172
+ def get_pg_type_oid(type_name)
173
+ type = PG_TYPES.find { |t| t[:typname] == type_name }&.dig(:oid)
174
+ type || 1043 # Default to varchar if type not found
175
+ end
176
+ end
177
+ end
data/lib/quill-sql.rb CHANGED
@@ -84,12 +84,11 @@ class Quill
84
84
  # If the task requires flags to be synthesized from tenants
85
85
  if FLAG_TASKS.include?(metadata[:task]) &&
86
86
  tenants&.first != ALL_TENANTS &&
87
- tenants&.first != SINGLE_TENANT &&
88
- (metadata[:task] != 'filter-options' ||
89
- !metadata[:reportId])
87
+ tenants&.first != SINGLE_TENANT
90
88
 
91
89
  response = post_quill('tenant-mapped-flags', {
92
90
  reportId: metadata[:reportId] || metadata[:dashboardItemId],
91
+ clientId: metadata[:clientId],
93
92
  dashboardName: metadata[:name],
94
93
  tenants: tenants.map { |tenant| TenantUtils.deep_camelize_keys(tenant) },
95
94
  flags: flags&.map { |flag| TenantUtils.deep_camelize_keys(flag) }
@@ -140,14 +139,18 @@ class Quill
140
139
  }
141
140
  end
142
141
 
143
- response = post_quill(metadata[:task], {
142
+ sdk_filters = filters&.map { |filter| FilterUtils.convert_custom_filter(filter) }
143
+
144
+ payload = {
144
145
  **metadata,
145
- sdkFilters: filters&.map { |filter| FilterUtils.convert_custom_filter(filter) },
146
146
  **pre_query_results,
147
147
  tenants: tenants.map { |tenant| TenantUtils.deep_camelize_keys(tenant) },
148
148
  flags: tenant_flags&.map { |flag| TenantUtils.deep_camelize_keys(flag) },
149
149
  viewQuery: metadata[:preQueries]&.first
150
- })
150
+ }
151
+ payload[:sdkFilters] = sdk_filters if sdk_filters # So it's undefined instead of nil
152
+
153
+ response = post_quill(metadata[:task], payload)
151
154
 
152
155
  return {
153
156
  status: "error",
data/lib/utils/tenants.rb CHANGED
@@ -2,8 +2,8 @@ module TenantUtils
2
2
  def self.extract_tenant_ids(tenants)
3
3
  if tenants[0].is_a?(String) || tenants[0].is_a?(Numeric)
4
4
  tenants
5
- elsif tenants[0].is_a?(Hash) && tenants[0].key?('tenant_ids')
6
- tenants[0]['tenant_ids']
5
+ elsif tenants[0].is_a?(Hash) && (tenants[0].key?('tenantIds') || tenants[0].key?(:tenantIds))
6
+ tenants[0]['tenantIds'] || tenants[0][:tenantIds]
7
7
  else
8
8
  raise 'Invalid format for tenants'
9
9
  end
@@ -12,8 +12,8 @@ module TenantUtils
12
12
  def self.extract_tenant_field(tenants, dashboard_owner)
13
13
  if tenants[0].is_a?(String) || tenants[0].is_a?(Numeric)
14
14
  dashboard_owner
15
- elsif tenants[0].is_a?(Hash) && tenants[0].key?('tenant_field')
16
- tenants[0]['tenant_field']
15
+ elsif tenants[0].is_a?(Hash) && (tenants[0].key?('tenantField') || tenants[0].key?(:tenantField))
16
+ tenants[0]['tenantField'] || tenants[0][:tenantField]
17
17
  else
18
18
  raise 'Invalid format for tenants'
19
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quill-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shawn Magee, Albert Yan
8
8
  - Sam Bishop
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-05 00:00:00.000000000 Z
11
+ date: 2025-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 7.0.0
33
+ version: 6.1.7
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 7.0.0
40
+ version: 6.1.7
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: click_house
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -135,6 +135,7 @@ files:
135
135
  - lib/db/cached_connection.rb
136
136
  - lib/db/clickhouse.rb
137
137
  - lib/db/db_helper.rb
138
+ - lib/db/postgres.rb
138
139
  - lib/models/filters.rb
139
140
  - lib/quill-sql.rb
140
141
  - lib/utils/run_query_processes.rb