dwh 0.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 +7 -0
- data/.rubocop.yml +36 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +130 -0
- data/Rakefile +42 -0
- data/docs/DWH/Adapters/Adapter.html +3053 -0
- data/docs/DWH/Adapters/Athena.html +1704 -0
- data/docs/DWH/Adapters/Boolean.html +121 -0
- data/docs/DWH/Adapters/Druid.html +1626 -0
- data/docs/DWH/Adapters/DuckDb.html +2012 -0
- data/docs/DWH/Adapters/MySql.html +1704 -0
- data/docs/DWH/Adapters/OpenAuthorizable/ClassMethods.html +265 -0
- data/docs/DWH/Adapters/OpenAuthorizable.html +1102 -0
- data/docs/DWH/Adapters/Postgres.html +2000 -0
- data/docs/DWH/Adapters/Snowflake.html +1662 -0
- data/docs/DWH/Adapters/SqlServer.html +2084 -0
- data/docs/DWH/Adapters/Trino.html +1835 -0
- data/docs/DWH/Adapters.html +129 -0
- data/docs/DWH/AuthenticationError.html +142 -0
- data/docs/DWH/Behaviors.html +767 -0
- data/docs/DWH/Capabilities.html +748 -0
- data/docs/DWH/Column.html +1115 -0
- data/docs/DWH/ConfigError.html +143 -0
- data/docs/DWH/ConnectionError.html +143 -0
- data/docs/DWH/DWHError.html +138 -0
- data/docs/DWH/ExecutionError.html +143 -0
- data/docs/DWH/Factory.html +1133 -0
- data/docs/DWH/Functions/Arrays.html +505 -0
- data/docs/DWH/Functions/Dates.html +1644 -0
- data/docs/DWH/Functions/ExtractDatePart.html +804 -0
- data/docs/DWH/Functions/Nulls.html +377 -0
- data/docs/DWH/Functions.html +846 -0
- data/docs/DWH/Logger.html +258 -0
- data/docs/DWH/OAuthError.html +138 -0
- data/docs/DWH/Settings.html +658 -0
- data/docs/DWH/StreamingStats.html +804 -0
- data/docs/DWH/Table.html +1260 -0
- data/docs/DWH/TableStats.html +583 -0
- data/docs/DWH/TokenExpiredError.html +142 -0
- data/docs/DWH/UnsupportedCapability.html +135 -0
- data/docs/DWH.html +220 -0
- data/docs/_index.html +471 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/file.README.html +210 -0
- data/docs/file.adapters.html +514 -0
- data/docs/file.creating-adapters.html +497 -0
- data/docs/file.getting-started.html +288 -0
- data/docs/file.usage.html +446 -0
- data/docs/file_list.html +79 -0
- data/docs/frames.html +22 -0
- data/docs/guides/adapters.md +445 -0
- data/docs/guides/creating-adapters.md +430 -0
- data/docs/guides/getting-started.md +225 -0
- data/docs/guides/usage.md +378 -0
- data/docs/index.html +210 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +2038 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/dwh/adapters/athena.rb +359 -0
- data/lib/dwh/adapters/druid.rb +267 -0
- data/lib/dwh/adapters/duck_db.rb +235 -0
- data/lib/dwh/adapters/my_sql.rb +235 -0
- data/lib/dwh/adapters/open_authorizable.rb +215 -0
- data/lib/dwh/adapters/postgres.rb +250 -0
- data/lib/dwh/adapters/snowflake.rb +489 -0
- data/lib/dwh/adapters/sql_server.rb +257 -0
- data/lib/dwh/adapters/trino.rb +213 -0
- data/lib/dwh/adapters.rb +363 -0
- data/lib/dwh/behaviors.rb +67 -0
- data/lib/dwh/capabilities.rb +39 -0
- data/lib/dwh/column.rb +79 -0
- data/lib/dwh/errors.rb +29 -0
- data/lib/dwh/factory.rb +125 -0
- data/lib/dwh/functions/arrays.rb +42 -0
- data/lib/dwh/functions/dates.rb +162 -0
- data/lib/dwh/functions/extract_date_part.rb +70 -0
- data/lib/dwh/functions/nulls.rb +31 -0
- data/lib/dwh/functions.rb +86 -0
- data/lib/dwh/logger.rb +50 -0
- data/lib/dwh/settings/athena.yml +77 -0
- data/lib/dwh/settings/base.yml +81 -0
- data/lib/dwh/settings/databricks.yml +51 -0
- data/lib/dwh/settings/druid.yml +59 -0
- data/lib/dwh/settings/duckdb.yml +44 -0
- data/lib/dwh/settings/mysql.yml +67 -0
- data/lib/dwh/settings/postgres.yml +30 -0
- data/lib/dwh/settings/redshift.yml +52 -0
- data/lib/dwh/settings/snowflake.yml +45 -0
- data/lib/dwh/settings/sqlserver.yml +80 -0
- data/lib/dwh/settings/trino.yml +77 -0
- data/lib/dwh/settings.rb +79 -0
- data/lib/dwh/streaming_stats.rb +69 -0
- data/lib/dwh/table.rb +105 -0
- data/lib/dwh/table_stats.rb +51 -0
- data/lib/dwh/version.rb +5 -0
- data/lib/dwh.rb +54 -0
- data/sig/dwh.rbs +4 -0
- metadata +231 -0
@@ -0,0 +1,497 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
File: Creating Custom Adapters
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.37
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
16
|
+
|
17
|
+
<script type="text/javascript">
|
18
|
+
pathId = "creating-adapters";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="file_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
<span class="title">File: Creating Custom Adapters</span>
|
41
|
+
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div id="search">
|
45
|
+
|
46
|
+
<a class="full_list_link" id="class_list_link"
|
47
|
+
href="class_list.html">
|
48
|
+
|
49
|
+
<svg width="24" height="24">
|
50
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
51
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
52
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
</svg>
|
54
|
+
</a>
|
55
|
+
|
56
|
+
</div>
|
57
|
+
<div class="clear"></div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div id="content"><div id='filecontents'><h1 id="creating-custom-adapters">Creating Custom Adapters</h1>
|
61
|
+
|
62
|
+
<p>The whole point of this library is to make adding a new database integration easy. With a few steps you can create your own adapter. If its generic, please contribute back to the project via PR.</p>
|
63
|
+
|
64
|
+
<p>This guide walks you through creating your own custom database adapter for DWH. Creating a new adapter involves extending the base adapter class, implementing required methods, and optionally creating custom settings.</p>
|
65
|
+
|
66
|
+
<h2 id="understanding-dwh-architecture">Understanding DWH Architecture</h2>
|
67
|
+
|
68
|
+
<p>DWH adapters have a simple, focused architecture:</p>
|
69
|
+
|
70
|
+
<ul>
|
71
|
+
<li><strong>5 Core Methods</strong>: Every adapter must implement 5 essential methods</li>
|
72
|
+
<li><strong>YAML Settings</strong>: Database-specific behavior controlled by YAML configuration</li>
|
73
|
+
<li><strong>Configuration Validation</strong>: Automatic validation of connection parameters</li>
|
74
|
+
<li><strong>Function Translation</strong>: SQL functions automatically translated to database-specific syntax</li>
|
75
|
+
</ul>
|
76
|
+
|
77
|
+
<h2 id="minimal-adapter-example">Minimal Adapter Example</h2>
|
78
|
+
|
79
|
+
<p>Here’s a minimal adapter implementation:</p>
|
80
|
+
|
81
|
+
<p>```ruby
|
82
|
+
module DWH
|
83
|
+
module Adapters
|
84
|
+
class MyCustomAdapter < Adapter
|
85
|
+
# Define required configuration parameters
|
86
|
+
config :host, String, required: true, message: ‘server host ip address or domain name’
|
87
|
+
config :port, Integer, required: false, default: 1234, message: ‘port to connect to’
|
88
|
+
config :database, String, required: true, message: ‘name of database to connect to’
|
89
|
+
config :username, String, required: true, message: ‘connection username’
|
90
|
+
config :password, String, required: false, default: nil, message: ‘connection password’</p>
|
91
|
+
|
92
|
+
<pre class="code ruby"><code class="ruby"> # Implement required methods
|
93
|
+
def connection
|
94
|
+
# Return your database connection object
|
95
|
+
# This is cached, so implement connection reuse here
|
96
|
+
@connection ||= create_connection
|
97
|
+
end
|
98
|
+
|
99
|
+
def tables(catalog: nil, schema: nil)
|
100
|
+
# Return array of DWH::Table objects
|
101
|
+
# Use catalog/schema for filtering if supported
|
102
|
+
end
|
103
|
+
|
104
|
+
def metadata(table_name, catalog: nil, schema: nil)
|
105
|
+
# Return single DWH::Table object with column information
|
106
|
+
end
|
107
|
+
|
108
|
+
def stats(table_name, date_column: nil, catalog: nil, schema: nil)
|
109
|
+
# Return DWH::TableStats object with row counts and date ranges
|
110
|
+
end
|
111
|
+
|
112
|
+
def execute(sql, format: :array, retries: 0)
|
113
|
+
# Execute SQL and return results in specified format
|
114
|
+
# Formats: :array, :object, :csv, :native
|
115
|
+
end
|
116
|
+
|
117
|
+
def execute_stream(sql, io, stats: nil)
|
118
|
+
# Execute SQL and stream results directly to IO object
|
119
|
+
end
|
120
|
+
|
121
|
+
def stream(sql, &block)
|
122
|
+
# Execute SQL and yield chunks to block
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def create_connection
|
128
|
+
# Your database-specific connection logic
|
129
|
+
MyDatabaseClient.connect(
|
130
|
+
host: config[:host],
|
131
|
+
port: config[:port],
|
132
|
+
database: config[:database],
|
133
|
+
username: config[:username],
|
134
|
+
password: config[:password]
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end end end
|
138
|
+
</code></pre>
|
139
|
+
|
140
|
+
<h1 id="register-your-adapter">Register your adapter</h1>
|
141
|
+
<p>DWH.register(:mycustom, DWH::Adapters::MyCustomAdapter)
|
142
|
+
```</p>
|
143
|
+
|
144
|
+
<h2 id="step-by-step-implementation">Step-by-Step Implementation</h2>
|
145
|
+
|
146
|
+
<h3 id="define-configuration-parameters">1. Define Configuration Parameters</h3>
|
147
|
+
|
148
|
+
<p>Use the <code>config</code> class method to define connection parameters:</p>
|
149
|
+
|
150
|
+
<p>```ruby
|
151
|
+
class MyCustomAdapter < Adapter
|
152
|
+
# Required parameters
|
153
|
+
config :host, String, required: true, message: ‘server host ip address or domain name’
|
154
|
+
config :database, String, required: true, message: ‘name of database to connect to’</p>
|
155
|
+
|
156
|
+
<p># Optional parameters with defaults
|
157
|
+
config :port, Integer, required: false, default: 5432, message: ‘port to connect to’
|
158
|
+
config :timeout, Integer, required: false, default: 30, message: ‘connection timeout’</p>
|
159
|
+
|
160
|
+
<p># Boolean parameters
|
161
|
+
config :ssl, Boolean, required: false, default: false, message: ‘use ssl connection’</p>
|
162
|
+
|
163
|
+
<p># Parameters with allowed values
|
164
|
+
config :auth_type, String, required: false, default: ‘basic’,
|
165
|
+
message: ‘authentication type’, allowed: %w[basic oauth token]
|
166
|
+
end
|
167
|
+
```</p>
|
168
|
+
|
169
|
+
<h3 id="implement-connection-management">2. Implement Connection Management</h3>
|
170
|
+
|
171
|
+
<p>```ruby
|
172
|
+
def connection
|
173
|
+
return @connection if @connection && connection_valid?</p>
|
174
|
+
|
175
|
+
<p>@connection = create_connection
|
176
|
+
end</p>
|
177
|
+
|
178
|
+
<p>private</p>
|
179
|
+
|
180
|
+
<p>def create_connection
|
181
|
+
# Example for HTTP-based database
|
182
|
+
Faraday.new(
|
183
|
+
url: “#protocol://#config[:host]:#config[:port]”,
|
184
|
+
headers: build_headers,
|
185
|
+
request: {
|
186
|
+
timeout: config[:timeout]
|
187
|
+
}
|
188
|
+
)
|
189
|
+
end</p>
|
190
|
+
|
191
|
+
<p>def build_headers
|
192
|
+
headers = { ‘Content-Type’ => ‘application/json’ }
|
193
|
+
headers[‘Authorization’] = “Bearer #config[:token]” if config[:token]
|
194
|
+
headers
|
195
|
+
end</p>
|
196
|
+
|
197
|
+
<p>def connection_valid?
|
198
|
+
# Implement connection health check
|
199
|
+
@connection&.get(‘/health’)&.success?
|
200
|
+
rescue
|
201
|
+
false
|
202
|
+
end
|
203
|
+
```</p>
|
204
|
+
|
205
|
+
<h3 id="implement-table-discovery">3. Implement Table Discovery</h3>
|
206
|
+
|
207
|
+
<p>```ruby
|
208
|
+
def tables(catalog: nil, schema: nil)
|
209
|
+
query = build_tables_query(catalog: catalog, schema: schema)
|
210
|
+
results = execute(query, format: :array)</p>
|
211
|
+
|
212
|
+
<p>results.map do |row|
|
213
|
+
DWH::Table.new(
|
214
|
+
physical_name: row[0],
|
215
|
+
schema: row[1] || ‘default’,
|
216
|
+
catalog: row[2],
|
217
|
+
table_type: row[3] || ‘TABLE’
|
218
|
+
)
|
219
|
+
end
|
220
|
+
end</p>
|
221
|
+
|
222
|
+
<p>private</p>
|
223
|
+
|
224
|
+
<p>def build_tables_query(catalog: nil, schema: nil)
|
225
|
+
query = “SHOW TABLES”</p>
|
226
|
+
|
227
|
+
<p>conditions = []
|
228
|
+
conditions « “FROM #catalog” if catalog
|
229
|
+
conditions « “LIKE ‘#schema.%’” if schema</p>
|
230
|
+
|
231
|
+
<p>query += “ #‘)” unless conditions.empty?
|
232
|
+
query
|
233
|
+
end
|
234
|
+
```</p>
|
235
|
+
|
236
|
+
<h3 id="implement-metadata-extraction">4. Implement Metadata Extraction</h3>
|
237
|
+
|
238
|
+
<p>```ruby
|
239
|
+
def metadata(table_name, catalog: nil, schema: nil)
|
240
|
+
# Parse table name if it includes schema/catalog
|
241
|
+
parsed = parse_table_name(table_name, catalog: catalog, schema: schema)</p>
|
242
|
+
|
243
|
+
<p>query = build_describe_query(parsed[:table], parsed[:schema], parsed[:catalog])
|
244
|
+
results = execute(query, format: :array)</p>
|
245
|
+
|
246
|
+
<p>columns = results.map do |row|
|
247
|
+
DWH::Column.new(
|
248
|
+
name: row[0],
|
249
|
+
data_type: row[1],
|
250
|
+
normalized_data_type: normalize_data_type(row[1]),
|
251
|
+
nullable: row[2] != ‘NO’,
|
252
|
+
default_value: row[3],
|
253
|
+
character_maximum_length: row[4],
|
254
|
+
numeric_precision: row[5],
|
255
|
+
numeric_scale: row[6]
|
256
|
+
)
|
257
|
+
end</p>
|
258
|
+
|
259
|
+
<p>DWH::Table.new(
|
260
|
+
physical_name: parsed[:table],
|
261
|
+
schema: parsed[:schema],
|
262
|
+
catalog: parsed[:catalog],
|
263
|
+
columns: columns
|
264
|
+
)
|
265
|
+
end
|
266
|
+
```</p>
|
267
|
+
|
268
|
+
<h3 id="implement-statistics-collection">5. Implement Statistics Collection</h3>
|
269
|
+
|
270
|
+
<p>```ruby
|
271
|
+
def stats(table_name, date_column: nil, catalog: nil, schema: nil)
|
272
|
+
parsed = parse_table_name(table_name, catalog: catalog, schema: schema)
|
273
|
+
full_table_name = build_full_table_name(parsed)</p>
|
274
|
+
|
275
|
+
<p># Get row count
|
276
|
+
count_query = “SELECT COUNT(*) FROM #full_table_name”
|
277
|
+
row_count = execute(count_query, format: :array).first.first</p>
|
278
|
+
|
279
|
+
<p># Get date range if date column provided
|
280
|
+
date_start = date_end = nil
|
281
|
+
if date_column
|
282
|
+
date_query = “SELECT MIN(#date_column), MAX(#date_column) FROM #full_table_name”
|
283
|
+
date_result = execute(date_query, format: :array).first
|
284
|
+
date_start, date_end = date_result
|
285
|
+
end</p>
|
286
|
+
|
287
|
+
<p>DWH::TableStats.new(
|
288
|
+
row_count: row_count,
|
289
|
+
date_start: date_start,
|
290
|
+
date_end: date_end
|
291
|
+
)
|
292
|
+
end
|
293
|
+
```</p>
|
294
|
+
|
295
|
+
<h3 id="implement-query-execution">6. Implement Query Execution</h3>
|
296
|
+
|
297
|
+
<p>```ruby
|
298
|
+
def execute(sql, format: :array, retries: 0)
|
299
|
+
response = connection.post(‘/query’, { sql: sql }.to_json)</p>
|
300
|
+
|
301
|
+
<p>raise DWH::ExecutionError, “Query failed: #responseresponse.body” unless response.success?</p>
|
302
|
+
|
303
|
+
<p>raw_data = JSON.parse(response.body)
|
304
|
+
format_results(raw_data, format)
|
305
|
+
rescue => e
|
306
|
+
if retries > 0
|
307
|
+
sleep(1)
|
308
|
+
execute(sql, format: format, retries: retries - 1)
|
309
|
+
else
|
310
|
+
raise DWH::ExecutionError, “Query execution failed: #ee.message”
|
311
|
+
end
|
312
|
+
end</p>
|
313
|
+
|
314
|
+
<p>def execute_stream(sql, io, stats: nil)
|
315
|
+
# For HTTP APIs, you might need to paginate or use streaming endpoints
|
316
|
+
offset = 0
|
317
|
+
limit = 10_000</p>
|
318
|
+
|
319
|
+
<p>loop do
|
320
|
+
paginated_sql = “#sql LIMIT #limit OFFSET #offset”
|
321
|
+
results = execute(paginated_sql, format: :array)</p>
|
322
|
+
|
323
|
+
<pre class="code ruby"><code class="ruby">break if results.empty?
|
324
|
+
|
325
|
+
results.each do |row|
|
326
|
+
csv_row = CSV.generate_line(row)
|
327
|
+
io.write(csv_row)
|
328
|
+
stats&.add_row(row)
|
329
|
+
end
|
330
|
+
|
331
|
+
offset += limit end end
|
332
|
+
</code></pre>
|
333
|
+
|
334
|
+
<p>def stream(sql, &block)
|
335
|
+
# Similar to execute_stream but yields chunks to block
|
336
|
+
offset = 0
|
337
|
+
limit = 10_000</p>
|
338
|
+
|
339
|
+
<p>loop do
|
340
|
+
paginated_sql = “#sql LIMIT #limit OFFSET #offset”
|
341
|
+
chunk = execute(paginated_sql, format: :array)</p>
|
342
|
+
|
343
|
+
<pre class="code ruby"><code class="ruby">break if chunk.empty?
|
344
|
+
|
345
|
+
yield chunk
|
346
|
+
offset += limit end end
|
347
|
+
</code></pre>
|
348
|
+
|
349
|
+
<p>private</p>
|
350
|
+
|
351
|
+
<p>def format_results(raw_data, format)
|
352
|
+
case format
|
353
|
+
when :array
|
354
|
+
raw_data[‘rows’]
|
355
|
+
when :object
|
356
|
+
columns = raw_data[‘columns’]
|
357
|
+
raw_data[‘rows’].map { |row| columns.zip(row).to_h }
|
358
|
+
when :csv
|
359
|
+
CSV.generate do |csv|
|
360
|
+
raw_data[‘rows’].each { |row| csv « row }
|
361
|
+
end
|
362
|
+
when :native
|
363
|
+
raw_data
|
364
|
+
else
|
365
|
+
raise ArgumentError, “Unsupported format: #format”
|
366
|
+
end
|
367
|
+
end
|
368
|
+
```</p>
|
369
|
+
|
370
|
+
<h2 id="creating-custom-settings">Creating Custom Settings</h2>
|
371
|
+
|
372
|
+
<h3 id="create-settings-file">1. Create Settings File</h3>
|
373
|
+
|
374
|
+
<p>Create by copying the <a href="https://github.com/stratasite/dwh/blob/main/lib/dwh/settings/base.yml">base settings file</a> to a relative directory like so:<code>settings/mycustom.yml</code></p>
|
375
|
+
|
376
|
+
<p>```yaml
|
377
|
+
# Override base settings for your database</p>
|
378
|
+
|
379
|
+
<h1 id="function-mappings">Function mappings</h1>
|
380
|
+
<p>truncate_date: “DATE_TRUNC(‘@unit’, @exp)”
|
381
|
+
date_literal: “DATE(‘@val’)”
|
382
|
+
cast: “CAST(@exp AS @type)”</p>
|
383
|
+
|
384
|
+
<h1 id="string-functions">String functions</h1>
|
385
|
+
<p>trim: “LTRIM(RTRIM(@exp))”
|
386
|
+
upper_case: “UPPER(@exp)”
|
387
|
+
lower_case: “LOWER(@exp)”</p>
|
388
|
+
|
389
|
+
<h1 id="null-handling">Null handling</h1>
|
390
|
+
<p>if_null: “ISNULL(@exp, @when_null)”
|
391
|
+
null_if: “CASE WHEN @exp = @target THEN NULL ELSE @exp END”</p>
|
392
|
+
|
393
|
+
<h1 id="capabilities">Capabilities</h1>
|
394
|
+
<p>supports_window_functions: true
|
395
|
+
supports_array_functions: false
|
396
|
+
supports_common_table_expressions: true
|
397
|
+
supports_temp_tables: false</p>
|
398
|
+
|
399
|
+
<h1 id="query-behavior">Query behavior</h1>
|
400
|
+
<p>temp_table_type: “subquery” # options: cte, subquery, temp
|
401
|
+
final_pass_measure_join_type: “inner” # inner, left, right, full</p>
|
402
|
+
|
403
|
+
<h1 id="custom-settings-for-your-database">Custom settings for your database</h1>
|
404
|
+
<p>custom_query_prefix: “/* Generated by DWH */”
|
405
|
+
max_query_length: 1000000
|
406
|
+
```</p>
|
407
|
+
|
408
|
+
<h3 id="custom-settings-location">2. Custom Settings Location</h3>
|
409
|
+
|
410
|
+
<p>```ruby
|
411
|
+
class MyCustomAdapter < Adapter
|
412
|
+
# Specify custom settings file location
|
413
|
+
settings_file_path “/path/to/my_custom_settings.yml”</p>
|
414
|
+
|
415
|
+
<p># … rest of implementation
|
416
|
+
end
|
417
|
+
```</p>
|
418
|
+
|
419
|
+
<h2 id="advanced-features">Advanced Features</h2>
|
420
|
+
|
421
|
+
<h3 id="error-handling">Error Handling</h3>
|
422
|
+
|
423
|
+
<p><code>ruby
|
424
|
+
def execute(sql, format: :array, retries: 0)
|
425
|
+
# Your execution logic
|
426
|
+
rescue MyDatabaseClient::ConnectionError => e
|
427
|
+
raise DWH::ConnectionError, "Database connection failed: #{e.message}"
|
428
|
+
rescue MyDatabaseClient::QueryError => e
|
429
|
+
raise DWH::ExecutionError, "Query execution failed: #{e.message}"
|
430
|
+
rescue => e
|
431
|
+
raise DWH::AdapterError, "Unexpected error: #{e.message}"
|
432
|
+
end
|
433
|
+
</code></p>
|
434
|
+
|
435
|
+
<h3 id="custom-function-translation">Custom Function Translation</h3>
|
436
|
+
|
437
|
+
<p>```ruby
|
438
|
+
def custom_function(expression, param1, param2)
|
439
|
+
# Access settings for function templates
|
440
|
+
template = settings[:custom_function] || “CUSTOM_FUNC(@exp, @p1, @p2)”</p>
|
441
|
+
|
442
|
+
<p>template.gsub(‘@exp’, expression)
|
443
|
+
.gsub(‘@p1’, param1.to_s)
|
444
|
+
.gsub(‘@p2’, param2.to_s)
|
445
|
+
end
|
446
|
+
```</p>
|
447
|
+
|
448
|
+
<h2 id="registration-and-usage">Registration and Usage</h2>
|
449
|
+
|
450
|
+
<h3 id="register-your-adapter-1">Register Your Adapter</h3>
|
451
|
+
|
452
|
+
<p>```ruby
|
453
|
+
# In your gem or application initialization
|
454
|
+
require ‘dwh’
|
455
|
+
require ‘my_custom_adapter’</p>
|
456
|
+
|
457
|
+
<p>DWH.register(:mycustom, DWH::Adapters::MyCustomAdapter)
|
458
|
+
```</p>
|
459
|
+
|
460
|
+
<h3 id="use-your-adapter">Use Your Adapter</h3>
|
461
|
+
|
462
|
+
<p>```ruby
|
463
|
+
# Create adapter instance
|
464
|
+
adapter = DWH.create(:mycustom, {
|
465
|
+
host: ‘database.example.com’,
|
466
|
+
port: 1234,
|
467
|
+
database: ‘analytics’,
|
468
|
+
username: ‘analyst’,
|
469
|
+
password: ‘secret’
|
470
|
+
})</p>
|
471
|
+
|
472
|
+
<h1 id="use-standard-dwh-interface">Use standard DWH interface</h1>
|
473
|
+
<p>tables = adapter.tables
|
474
|
+
metadata = adapter.metadata(‘users’)
|
475
|
+
results = adapter.execute(“SELECT COUNT(*) FROM users”)
|
476
|
+
```</p>
|
477
|
+
|
478
|
+
<h2 id="examples-to-study">Examples to Study</h2>
|
479
|
+
|
480
|
+
<p>Look at existing adapters for implementation patterns:</p>
|
481
|
+
|
482
|
+
<ul>
|
483
|
+
<li><strong>PostgreSQL</strong> (<code>lib/dwh/adapters/postgres.rb</code>) - RDBMS with full SQL support</li>
|
484
|
+
<li><strong>Druid</strong> (<code>lib/dwh/adapters/druid.rb</code>) - HTTP API-based adapter</li>
|
485
|
+
<li><strong>DuckDB</strong> (<code>lib/dwh/adapters/duck_db.rb</code>) - Embedded database adapter</li>
|
486
|
+
</ul>
|
487
|
+
</div></div>
|
488
|
+
|
489
|
+
<div id="footer">
|
490
|
+
Generated on Fri Aug 22 08:31:21 2025 by
|
491
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
492
|
+
0.9.37 (ruby-3.4.4).
|
493
|
+
</div>
|
494
|
+
|
495
|
+
</div>
|
496
|
+
</body>
|
497
|
+
</html>
|