db2_query 0.2.3 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +472 -124
- data/Rakefile +3 -2
- data/lib/db2_query/base.rb +15 -5
- data/lib/db2_query/config.rb +20 -17
- data/lib/db2_query/core.rb +79 -60
- data/lib/db2_query/db_client.rb +56 -0
- data/lib/db2_query/db_connection.rb +68 -0
- data/lib/db2_query/db_statements.rb +87 -0
- data/lib/db2_query/definitions.rb +93 -0
- data/lib/db2_query/error.rb +72 -7
- data/lib/db2_query/field_type.rb +31 -0
- data/lib/db2_query/helper.rb +50 -0
- data/lib/db2_query/logger.rb +52 -0
- data/lib/db2_query/query.rb +128 -0
- data/lib/db2_query/railtie.rb +5 -10
- data/lib/db2_query/result.rb +51 -31
- data/lib/db2_query/sql_statement.rb +34 -0
- data/lib/db2_query/tasks/database.rake +2 -46
- data/lib/db2_query/tasks/init.rake +1 -1
- data/lib/db2_query/tasks/initializer.rake +2 -34
- data/lib/db2_query/tasks/templates/database.rb.tt +19 -0
- data/lib/db2_query/tasks/templates/initializer.rb.tt +8 -0
- data/lib/db2_query/tasks.rb +29 -0
- data/lib/db2_query/type/binary.rb +19 -0
- data/lib/db2_query/type/boolean.rb +41 -0
- data/lib/db2_query/type/date.rb +34 -0
- data/lib/db2_query/type/decimal.rb +15 -0
- data/lib/db2_query/type/integer.rb +15 -0
- data/lib/db2_query/type/string.rb +30 -0
- data/lib/db2_query/type/text.rb +11 -0
- data/lib/db2_query/type/time.rb +30 -0
- data/lib/db2_query/type/timestamp.rb +30 -0
- data/lib/db2_query/type/value.rb +29 -0
- data/lib/db2_query/version.rb +2 -2
- data/lib/db2_query.rb +42 -18
- data/lib/rails/generators/query/USAGE +15 -0
- data/lib/rails/generators/query/query_generator.rb +70 -0
- data/lib/rails/generators/query/templates/query.rb.tt +26 -0
- data/lib/rails/generators/query/templates/query_definitions.rb.tt +18 -0
- data/lib/rails/generators/query/templates/unit_test.rb.tt +9 -0
- metadata +74 -36
- data/lib/db2_query/bind.rb +0 -6
- data/lib/db2_query/connection.rb +0 -164
- data/lib/db2_query/connection_handling.rb +0 -112
- data/lib/db2_query/database_statements.rb +0 -89
- data/lib/db2_query/formatter.rb +0 -27
- data/lib/db2_query/odbc_connector.rb +0 -44
data/README.md
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# Db2Query
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/db2_query.svg)](https://badge.fury.io/rb/db2_query)
|
4
4
|
|
5
|
-
A Rails
|
5
|
+
A Rails 5 & Rails 6 plugin for handling Db2 SQL database `SIUD` statement (`SELECT`, `INSERT`, `UPDATE`, `DELETE`) by using ODBC connection.
|
6
6
|
|
7
|
-
|
7
|
+
Note: Tested at Rails 5.2.6 and Rails 6.1.4
|
8
|
+
|
9
|
+
## 1. Installation
|
8
10
|
Add this line to your application's Gemfile:
|
9
11
|
|
10
12
|
```ruby
|
@@ -20,170 +22,512 @@ Or install it yourself as:
|
|
20
22
|
```bash
|
21
23
|
$ gem install db2_query
|
22
24
|
```
|
23
|
-
|
24
|
-
|
25
|
-
Execute init task at the app root
|
25
|
+
## 2. Initialization
|
26
|
+
Execute **db2query:init** task at the app root to create database configurations and initializer file.
|
26
27
|
```bash
|
27
28
|
$ rake db2query:init
|
29
|
+
create config/db2query.yml
|
30
|
+
create config/initializers/db2query.rb
|
28
31
|
```
|
29
|
-
DB2Query will generate two required files:
|
30
|
-
- `config/db2query_database.yml`
|
31
|
-
- `config/initializers/db2query.rb`
|
32
32
|
|
33
|
-
|
33
|
+
Complete the configurations by editing the files according to your application requirement.
|
34
34
|
|
35
|
-
|
35
|
+
### Database Configuration
|
36
|
+
File **config/db2query.yml** consist of DSN/database name and connection pool config:
|
37
|
+
```yml
|
38
|
+
development:
|
39
|
+
dsn: LIBDEV
|
40
|
+
idle: 5
|
41
|
+
pool: 5
|
42
|
+
timeout: 5
|
43
|
+
test:
|
44
|
+
dsn: LIBTEST
|
45
|
+
idle: 5
|
46
|
+
pool: 5
|
47
|
+
timeout: 5
|
48
|
+
production:
|
49
|
+
dsn: LIBPROD
|
50
|
+
idle: 5
|
51
|
+
pool: 5
|
52
|
+
timeout: 5
|
53
|
+
```
|
36
54
|
|
37
|
-
|
55
|
+
Key **idle** is a **client** idle maximum limit value (in minutes) to avoid the client being disconnected by the host server. Setting this value to zero will lead to an "ODBC driver Communication Link Failure. Comm rc 10054 . [CWBCO1047](https://www.ibm.com/support/pages/cwbco1047-any-function-uses-database-host-server)" error after your application idle in a certain period of time.
|
38
56
|
|
39
|
-
|
57
|
+
[**Ensure**](https://github.com/yohaneslumentut/db2_query/wiki/DB2-ODBC-Connection#verify-odbc-connection) that **unixodbc** has been installed and test your connection first by using **isql** commands.
|
40
58
|
|
41
|
-
###
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
```yml
|
46
|
-
development:
|
47
|
-
primary: # Connection String Example
|
48
|
-
conn_string:
|
49
|
-
driver: DB2
|
50
|
-
database: SAMPLE
|
51
|
-
dbalias: SAMPLE
|
52
|
-
hostname: LOCALHOST
|
53
|
-
currentschema: LIBTEST
|
54
|
-
port: "0"
|
55
|
-
protocol: IPC
|
56
|
-
uid: <%= ENV["DB2EC_UID"] %>
|
57
|
-
pwd: <%= ENV["DB2EC_PWD"] %>
|
58
|
-
secondary: # DSN Example
|
59
|
-
dsn: iseries
|
60
|
-
uid: <%= ENV["ISERIES_UID"] %>
|
61
|
-
pwd: <%= ENV["ISERIES_PWD"] %>
|
62
|
-
```
|
63
|
-
|
64
|
-
Ensure that `unixodbc` have been installed and test your connection first by using `isql` commands.
|
59
|
+
### Initializer File
|
60
|
+
This file is used by **Db2Query::Base** to load **field types** configurations and establish a **connection** instance.
|
61
|
+
```ruby
|
62
|
+
# app_root/config/initializers/db2query.rb
|
65
63
|
|
66
|
-
|
64
|
+
require "db2_query"
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
66
|
+
Db2Query::Base.initiation do |base|
|
67
|
+
base.set_field_types # or base.set_field_types(CUSTOM_FIELD_TYPES) if you have CUSTOM TYPES
|
68
|
+
base.establish_connection
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### Custom Field Type
|
73
|
+
**FieldTypes** are classes that are used by **Db2Query** to format the data before sending it to the database by using `serialize` method and `deserialize` the returned query result data by converting the **query result** before consumed by your **Rails application**. Both `serialize` and `deserialize` operations are only applied when you provide **QueryDefinitions** on your query.
|
74
|
+
|
75
|
+
By default, there are ten field types that can be used in your [query definitions](#32-querydefinitions) :
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
DEFAULT_FIELD_TYPES = {
|
79
|
+
binary: Db2Query::Type::Binary,
|
80
|
+
boolean: Db2Query::Type::Boolean,
|
81
|
+
string: Db2Query::Type::String,
|
82
|
+
varchar: Db2Query::Type::String,
|
83
|
+
longvarchar: Db2Query::Type::String,
|
84
|
+
decimal: Db2Query::Type::Decimal,
|
85
|
+
integer: Db2Query::Type::Integer,
|
86
|
+
date: Db2Query::Type::Date,
|
87
|
+
time: Db2Query::Type::Time,
|
88
|
+
timestamp: Db2Query::Type::Timestamp
|
89
|
+
}
|
90
|
+
```
|
91
|
+
You can use your own Field type class by extending **Db2Query::Type::Value** class. For example:
|
92
|
+
```ruby
|
93
|
+
class CustomTypeClass < Db2Query::Type::Value
|
94
|
+
# Method to convert data from ruby type value into data that is understood by Db2
|
95
|
+
def serialize(value)
|
96
|
+
# Your logic
|
97
|
+
end
|
98
|
+
|
99
|
+
# Method to convert Db2 database output data type that is recognized by your rails app
|
100
|
+
def deserialize(value)
|
101
|
+
# Your logic
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
Then put the classes into a field types hash constant and load it into the **Db2Query::Base** by using **set_field_types** method in the initializer file.
|
106
|
+
```ruby
|
107
|
+
# app_root/config/initializers/db2query.rb
|
108
|
+
|
109
|
+
require "db2_query"
|
110
|
+
|
111
|
+
CUSTOM_FIELD_TYPES = {
|
112
|
+
binary: CustomBinaryTypeClass
|
113
|
+
integer: CustomIntegerTypeClass
|
114
|
+
string: CustomStringTypeClass
|
115
|
+
...
|
116
|
+
}
|
117
|
+
|
118
|
+
Db2Query::Base.initiation do |base|
|
119
|
+
base.set_field_types(CUSTOM_FIELD_TYPES)
|
120
|
+
base.establish_connection
|
121
|
+
end
|
122
|
+
|
123
|
+
```
|
124
|
+
|
125
|
+
## 3. Usage
|
126
|
+
|
127
|
+
Once you completely do the [**Installation**](#1-installation) & [**Initialization**](#2-initialization) steps, basically you has been ready to use **Db2Query::Base**. There are three additional rules that help **Db2Query** run properly: **SQL Convention**, **Field Type Convention**, and **Argument Key Convention**.
|
128
|
+
|
129
|
+
**SQL Convention**:
|
130
|
+
> Dollar symbol **$** is used as the prefix of all column names **in the WHERE clause** of provided **Parameterized Query** SQL string. It is used as a pointer in the query arguments key and value binding process. We have to provide it manually in the SQL string of each **Parameterized Query**. Here, **Parameterized Query** is used to minimize SQL injection risks.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# SQL Convention Examples
|
134
|
+
# Example of Parameterized Query SQL usage
|
135
|
+
|
136
|
+
Db2Query::Base.query("SELECT * FROM USERS WHERE $email = ?", "my_account@email.com")
|
137
|
+
|
138
|
+
# Example of Normal SQL usage
|
139
|
+
|
140
|
+
Db2Query::Base.query("SELECT * FROM USERS WHERE email = 'my_account@email.com'")
|
141
|
+
```
|
142
|
+
|
143
|
+
**Field Type Convention**:
|
144
|
+
> Query definition's **field_name** written in **query_definition** block must be in downcased format.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# Field Type Convention Example
|
148
|
+
|
149
|
+
module Definitions
|
150
|
+
class UsersQueryDefinitions < Db2Query::Definitions
|
151
|
+
def describe
|
152
|
+
query_definition :all do |c|
|
153
|
+
c.id :integer
|
154
|
+
c.first_name :varchar
|
155
|
+
c.last_name :varchar
|
156
|
+
c.email :varchar
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
80
161
|
```
|
81
162
|
|
82
|
-
|
83
|
-
|
163
|
+
**Argument Key Convention**:
|
164
|
+
> The letter case of a **Named Argument** key that passed into a query, has to follow its parameter letter case format that is written in the SQL. The argument key is case-sensitive. If the parameter in your SQL is written in downcase format, then your argument key has to be in downcase format too, and vice versa.
|
84
165
|
|
85
|
-
### Basic Usage
|
86
|
-
Create query class that inherit from `DB2Query::Base` in `app/queries` folder
|
87
166
|
```ruby
|
88
|
-
|
167
|
+
# Argument Key Convention Example
|
168
|
+
|
169
|
+
class MyQuery < Db2Query::Base
|
170
|
+
...
|
89
171
|
query :find_by, <<-SQL
|
90
|
-
SELECT * FROM
|
172
|
+
SELECT * FROM USERS WHERE $id = ?
|
91
173
|
SQL
|
174
|
+
...
|
92
175
|
end
|
93
176
|
|
94
|
-
|
95
|
-
query :id_greater_than, -> id {
|
96
|
-
exec_query({}, "SELECT * FROM LIBTEST.USERS WHERE id > ?", [id])
|
97
|
-
}
|
177
|
+
MyQuery.find_user_by_id id: 10000
|
98
178
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
179
|
+
```
|
180
|
+
|
181
|
+
|
182
|
+
### 3.1 Basic Usage
|
183
|
+
|
184
|
+
#### Base Class Query Methods
|
185
|
+
|
186
|
+
##### #query(sql, args)
|
187
|
+
A raw query to perform a `connection.run(sql, args)` operation and returns an array of hashes representing each row record being executed.
|
188
|
+
```ruby
|
189
|
+
Db2Query::Base.query("SELECT * FROM USERS WHERE $id < ?", 10003)
|
190
|
+
=> [{:id=>10000, :first_name=>"Taisha", :last_name=>"Kutch", :email=>"willie.lesch@toy.org"}, {:id=>10001, :first_name=>"Setsuko", :last_name=>"Kutch", :email=>"thelma@purdy.co"}, {:id=>10002, :first_name=>"Trina", :last_name=>"Mayer", :email=>"dorsey_upton@flatley-gulgowski.name"}]
|
191
|
+
```
|
192
|
+
|
193
|
+
##### #query_rows(sql)
|
194
|
+
Execute the `SELECT Statement SQL` and returns collections of arrays consisting of row values.
|
195
|
+
```ruby
|
196
|
+
Db2Query::Base.query_rows("SELECT * FROM USERS WHERE id < 10003")
|
197
|
+
=> [[10000, "Taisha", "Kutch", "willie.lesch@toy.org"], [10001, "Setsuko", "Kutch", "thelma@purdy.co"], [10002, "Trina", "Mayer", "dorsey_upton@flatley-gulgowski.name"]]
|
198
|
+
```
|
199
|
+
|
200
|
+
##### #query_value(sql)
|
201
|
+
Execute the `SELECT Statement SQL` and returns the first value of the query results first row.
|
202
|
+
```ruby
|
203
|
+
Db2Query::Base.query_value("SELECT * FROM USERS WHERE id < 10003")
|
204
|
+
=> 10000
|
205
|
+
```
|
206
|
+
|
207
|
+
##### #query_values(sql)
|
208
|
+
Execute the `SELECT Statement SQL` and returns a collection of the first value of each query result rows.
|
209
|
+
```ruby
|
210
|
+
Db2Query::Base.query_values("SELECT * FROM USERS WHERE id < 10003")
|
211
|
+
=> [10000, 10001, 10002]
|
212
|
+
```
|
213
|
+
|
214
|
+
##### #execute(sql, args)
|
215
|
+
A method to execute `DUI Statement SQL` by using `connection.do(sql, args)`
|
216
|
+
```ruby
|
217
|
+
Db2Query::Base.execute("DELETE FROM users WHERE $id = ?", 10000)
|
218
|
+
=> -1
|
219
|
+
```
|
220
|
+
|
221
|
+
### 3.2 QueryDefinitions
|
222
|
+
|
223
|
+
#### 3.2.1 Query Field Definitions
|
224
|
+
|
225
|
+
QueryDefinitions is helpful when you need formatter methods that **serialize** the data before it being sent to the database and **deserialize** database output data before being consumed by **Rails application**. The real examples are **Binary** and **Boolean** field types.
|
226
|
+
At **Db2Query::Type::Binary**, the data `unpacked` by `serialize` method before sending to the database and do `deserialize` operation to `pack` the database returned data.
|
227
|
+
QueryDefinition can be used as **Query Schema** where the **field types** of a query are outlined. The field-type written in QueryDefinition has to follow the **Field Type Convention**.
|
228
|
+
|
229
|
+
A QueryDefinitions reside in `app_root/app/queries/definitions` directory. It is automatically created when you create your query by running `rails g query query_name` [**generator**](#33-generator) command. The QueryDefinitions class can be defined as follow:
|
230
|
+
```ruby
|
231
|
+
# app_root/app/queries/definitions/your_query_definitions.rb
|
232
|
+
module Definitions
|
233
|
+
class YourQueryDefinitions < Db2Query::Definitions
|
234
|
+
def describe # method that is used by Db2Query to describe your query definition
|
235
|
+
query_definition :your_first_query_name do |c|
|
236
|
+
c.field_name :field_type, options
|
237
|
+
...
|
238
|
+
end
|
239
|
+
|
240
|
+
query_definition :your_next_query_name do |c|
|
241
|
+
c.field_name :field_type, options
|
242
|
+
...
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
104
246
|
end
|
105
247
|
```
|
248
|
+
For Example:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
# app_root/app/queries/definitions/users_query_definitions.rb
|
252
|
+
|
253
|
+
module Definitions
|
254
|
+
class UsersQueryDefinitions < Db2Query::Definitions
|
255
|
+
def describe
|
256
|
+
query_definition :all do |c|
|
257
|
+
c.id :integer
|
258
|
+
c.first_name :varchar
|
259
|
+
c.last_name :varchar
|
260
|
+
c.email :varchar
|
261
|
+
end
|
262
|
+
|
263
|
+
query_definition :insert do |c|
|
264
|
+
c.id :integer
|
265
|
+
c.first_name :varchar
|
266
|
+
c.last_name :varchar
|
267
|
+
c.email :varchar
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
106
272
|
|
107
|
-
|
108
|
-
|
109
|
-
2. Body (can be an SQL statement or lamda).
|
273
|
+
```
|
274
|
+
#### 3.2.2 Query Argument Types
|
110
275
|
|
111
|
-
|
276
|
+
Sometimes, the `query arguments` do not exist in query definitions fields. In such a case, we have to provide `query argument types` at the Query class.
|
112
277
|
|
113
|
-
Or use a normal sql method (don't forget the `_sql` suffix)
|
114
278
|
```ruby
|
115
|
-
|
116
|
-
|
117
|
-
|
279
|
+
module NameSpace
|
280
|
+
class QueryName < Db2Query::Base
|
281
|
+
arguments :user_by_email, { email: :string, trim: true }
|
282
|
+
|
283
|
+
def user_by_email_sql
|
284
|
+
"SELECT id, first_name, last_name FROM USERS WHERE $email = ?"
|
285
|
+
end
|
118
286
|
end
|
119
287
|
end
|
120
288
|
```
|
121
|
-
|
289
|
+
|
290
|
+
### 3.3 Generator
|
291
|
+
|
292
|
+
Create query class by using `rails g query NAME` commands. For example:
|
293
|
+
|
122
294
|
```bash
|
123
|
-
|
124
|
-
|
125
|
-
|
295
|
+
$ rails g query NameSpace::Name --defines=first_query --queries=next_query --lambdas=last_query
|
296
|
+
create app/queries/name_space/name_query.rb
|
297
|
+
create app/queries/definitions/name_space/name_query_definitions.rb
|
298
|
+
create test/queries/name_space/name_query_test.rb
|
126
299
|
```
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
300
|
+
This will create `app/queries/name_space/name_query.rb` file in `app/queries` directory.
|
301
|
+
|
302
|
+
```ruby
|
303
|
+
module NameSpace
|
304
|
+
class Name < Db2Query::Base
|
305
|
+
def first_query_sql
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
query :next_query, <<-SQL
|
310
|
+
|
311
|
+
SQL
|
312
|
+
|
313
|
+
query :last_query, -> {
|
314
|
+
|
315
|
+
}
|
316
|
+
end
|
317
|
+
end
|
132
318
|
```
|
133
319
|
|
134
|
-
### Formatter
|
135
|
-
In order to get different result column format, a query result can be reformatted by add a formatter class that inherit `DB2Query::AbstractFormatter` then register at `config\initializers\db2query.rb`
|
136
320
|
```ruby
|
137
|
-
|
321
|
+
# app_root/app/queries/definitions/name_space/name_query.rb
|
322
|
+
|
323
|
+
module Definitions
|
324
|
+
module NameSpace
|
325
|
+
class NameQueryDefinition < Db2Query::Definitions
|
326
|
+
def describe # method that is used by Db2Query to describe your query definition
|
327
|
+
query_definition :first_query do |c|
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
query_definition :next_query do |c|
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
query_definition :last_query do |c|
|
138
336
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
"Dr." + value
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
143
340
|
end
|
144
341
|
end
|
342
|
+
```
|
343
|
+
|
344
|
+
Please run `rails g query --help` to get more information on how to use the file generator.
|
345
|
+
|
346
|
+
### 3.4 Queries Methods
|
347
|
+
|
348
|
+
In a **Query** class that extends **Db2Query::Base** class, there are 3 ways of query implementation:
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
class MyQuery < Db2Query::Base
|
352
|
+
# 1. Plain Query (--defines)
|
353
|
+
def query_name_sql
|
354
|
+
"YOUR AMAZING SQL STATEMENT STRING"
|
355
|
+
end
|
356
|
+
|
357
|
+
# 2. String Query (--queries)
|
358
|
+
query :query_name, <<-SQL
|
359
|
+
YOUR AMAZING SQL STATEMENT
|
360
|
+
SQL
|
361
|
+
|
362
|
+
# 3. Lambda Query (--lambdas)
|
363
|
+
query :query_name, -> args {
|
364
|
+
# implement fetch, fetch_list, and exec_query
|
365
|
+
fetch("YOUR AMAZING SQL", args)
|
366
|
+
}
|
367
|
+
end
|
368
|
+
```
|
369
|
+
|
370
|
+
#### 3.4.1 Plain Query (--defines)
|
371
|
+
Query implementation that uses the plain method. The method name must have a `_sql` suffix and return SQL statement string.
|
372
|
+
|
373
|
+
Example:
|
374
|
+
```ruby
|
375
|
+
class MyQuery < Db2Query::Base
|
376
|
+
def all_users_sql
|
377
|
+
"SELECT * FROM USERS"
|
378
|
+
end
|
145
379
|
|
146
|
-
|
147
|
-
|
148
|
-
|
380
|
+
def find_user_by_id_sql
|
381
|
+
"SELECT * FROM USERS WHERE $id = ?"
|
382
|
+
end
|
149
383
|
end
|
150
384
|
```
|
151
|
-
|
385
|
+
|
386
|
+
#### 3.4.2 String Query (--queries)
|
387
|
+
Query implementation that uses the built-in `query` method. The input arguments consist of `query_name` symbol and SQL statement
|
388
|
+
|
389
|
+
Example:
|
152
390
|
```ruby
|
153
|
-
class
|
154
|
-
|
391
|
+
class MyQuery < Db2Query::Base
|
392
|
+
query :all_users, <<-SQL
|
393
|
+
SELECT * FROM USERS
|
394
|
+
SQL
|
395
|
+
|
396
|
+
query :find_user_by_id, <<-SQL
|
397
|
+
SELECT * FROM USERS WHERE $id = ?
|
398
|
+
SQL
|
155
399
|
end
|
156
400
|
```
|
157
|
-
|
401
|
+
|
402
|
+
#### 3.4.3 Lambda Query (--lambdas)
|
403
|
+
Query implementation that uses the built-in `query` method. The input arguments consist of the `query_name` symbol and a lambda function. We have to pass `args` as the arguments of a lambda function. Do not change the `args` with let's say `-> id, email { ... }`. Just leave it written as `args`. The `args` is used by `Db2Query::Base` to store `query_name` and the other `arg` inputs.
|
404
|
+
|
405
|
+
Example:
|
406
|
+
```ruby
|
407
|
+
class MyQuery < Db2Query::Base
|
408
|
+
query :all_users, -> args {
|
409
|
+
fetch("SELECT * FROM USERS", args)
|
410
|
+
}
|
411
|
+
|
412
|
+
query :find_user_by_id, -> args {
|
413
|
+
fetch("SELECT * FROM USERS WHERE $id = ?", args)
|
414
|
+
}
|
415
|
+
end
|
416
|
+
```
|
417
|
+
|
418
|
+
Then you can call all three example with the same methods:
|
419
|
+
|
158
420
|
```bash
|
159
|
-
|
160
|
-
SQL
|
161
|
-
=> #<
|
421
|
+
irb(main):001:0> MyQuery.all_users
|
422
|
+
SQL (2.7ms) SELECT * FROM USERS
|
423
|
+
=> #<Db2Query::Result [#<Record id: 10000, first_name: Yohanes, ...]>
|
424
|
+
|
425
|
+
irb(main):001:0> MyQuery.find_user_by_id 10000
|
426
|
+
SQL (3.0ms) SELECT * FROM USERS WHERE id = ? [["id", 10000]]
|
427
|
+
=> #<Db2Query::Result [#<Record id: 10004, first_name: Yohanes, ...]>
|
162
428
|
```
|
429
|
+
If you pass a key-value argument into query, the key has to follow **Argument Key Convention**
|
163
430
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
431
|
+
```bash
|
432
|
+
irb(main):001:0> MyQuery.find_user_by_id(id: 10000)
|
433
|
+
SQL (3.0ms) SELECT * FROM USERS WHERE id = ? [["id", 10000]]
|
434
|
+
=> #<Db2Query::Result [#<Record id: 10004, first_name: Yohanes, ...]>
|
435
|
+
|
436
|
+
```
|
168
437
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
438
|
+
And use it at your application
|
439
|
+
```ruby
|
440
|
+
users = MyQuery.all
|
441
|
+
user_records = users.records
|
442
|
+
user_1 = user_records.first
|
443
|
+
user_1.id # => 10000
|
444
|
+
user_1.first_name # => "Yohanes"
|
445
|
+
user_1.last_name # => "Lumentut"
|
446
|
+
user_1.email # => "yohanes@github.com"
|
447
|
+
|
448
|
+
user_1 == users.record # => true
|
449
|
+
|
450
|
+
user = MyQuery.find_user_by_id id: 10000
|
451
|
+
user.id # => 10000
|
452
|
+
user.first_name # => "Yohanes"
|
453
|
+
user.last_name # => "Lumentut"
|
454
|
+
user.email # => "yohanes@github.com"
|
455
|
+
```
|
177
456
|
|
178
|
-
###
|
457
|
+
### 3.5 SQL extension (`@extension`)
|
458
|
+
For the sake of reusable SQL string, we can reuse the most commonly used SQL part by implementing `sql_with_extension` methods with an SQL string argument contain `@extension` pointer at SQL statement.
|
459
|
+
|
460
|
+
```ruby
|
461
|
+
class MyQuery < Db2Query::Base
|
462
|
+
# reusable SQL
|
463
|
+
_SQL = -> extension {
|
464
|
+
sql_with_extension("SELECT * FROM USERS WHERE @extension", extension)
|
465
|
+
}
|
466
|
+
|
467
|
+
# implementation
|
468
|
+
query :user_by_email, _SQL.("$email = ?")
|
469
|
+
end
|
470
|
+
```
|
471
|
+
```bash
|
472
|
+
irb(main):001:0> MyQuery.user_by_email email: "yohanes@github.com"
|
473
|
+
SQL (2.7ms) SELECT * FROM USERS email = ? [["email", "yohanes@github.com"]]
|
474
|
+
=> #<Db2Query::Result [#<Record id: 10000, first_name: Yohanes, ...]>
|
475
|
+
```
|
476
|
+
```ruby
|
477
|
+
user = MyQuery.user_by_email "yohanes@github.com"
|
478
|
+
user.id # => 10000
|
479
|
+
user.first_name # => "Yohanes"
|
480
|
+
user.last_name # => "Lumentut"
|
481
|
+
user.email # => "yohanes@github.com"
|
482
|
+
```
|
483
|
+
### 3.6 List input (`@list`)
|
484
|
+
For an array consist list of inputs, we can use `fetch_list` method and `@list` pointer at the SQL statement.
|
485
|
+
|
486
|
+
```ruby
|
487
|
+
class MyQuery < Db2Query::Base
|
488
|
+
query :user_by_ids, -> args {
|
489
|
+
fetch_list("SELECT * FROM USERS WHERE ID IN (@list)", args)
|
490
|
+
}
|
491
|
+
end
|
492
|
+
```
|
493
|
+
```bash
|
494
|
+
irb(main):007:0> MyQuery.user_by_ids [10000,10001,10002]
|
495
|
+
SQL (2.8ms) SELECT * FROM USERS WHERE ID IN ('10000', '10001', '10002')
|
496
|
+
=> #<Db2Query::Result [#<Record id: 10000, name: "Carol", last_name: "Danvers", email: "captain.marvel@marvel.universe.com">, #<Record id: 10001, first_name: "Natasha", last_name: "Romanova", email: "black.widow@marvel.universe">, #<Record id: 10002, first_name: "Wanda", last_name: "Maximoff", email: "scarlet.witch@marvel.universe.com">]>
|
497
|
+
|
498
|
+
```
|
499
|
+
```ruby
|
500
|
+
users = MyQuery.user_by_ids [10000,10001,10002]
|
501
|
+
user = users.first
|
502
|
+
user == users.record # => true
|
503
|
+
|
504
|
+
user.id # => 10000
|
505
|
+
user.first_name # => "Carol"
|
506
|
+
user.last_name # => "Danvers"
|
507
|
+
user.email # => "captain.marvel@marvel.universe.com"
|
508
|
+
```
|
509
|
+
|
510
|
+
### 3.7 Formatter
|
511
|
+
For the latest version of **Db2Query**, there is no more **Db2Query::Formatter** class. We can implement our formater into **deserialize** method of our [**QueryDefinitions**](#32-querydefinitions).
|
512
|
+
|
513
|
+
If you upgrade from the previous version, you have to run **`rake db2query:init`** again to override the initializer. Please create a backup of your Formatter classes before you do this operation. Then you can implement your Formatter methods into your **QueryDefinitions**.
|
514
|
+
|
515
|
+
## 4. Available Result Object methods
|
516
|
+
`Db2Query::Result` inherit all `ActiveRecord::Result` methods with additional custom methods:
|
517
|
+
1. `records` to convert query result into an array of Result query's Record objects.
|
518
|
+
2. `record` to get the first Record Object of Result query.
|
519
|
+
3. `to_h` to convert query result into an array of hashes with symbolized keys.
|
520
|
+
|
521
|
+
## 5. ActiveRecord Combination
|
522
|
+
|
523
|
+
Create an abstract class that inherits from `ActiveRecord::Base`. We have to implement `splat` operator correctly at the arguments to make it works.
|
179
524
|
|
180
|
-
Create an abstract class that inherit from `ActiveRecord::Base`
|
181
525
|
```ruby
|
182
526
|
class Db2Record < ActiveRecord::Base
|
183
527
|
self.abstract_class = true
|
184
528
|
|
185
|
-
def self.query(sql,
|
186
|
-
|
529
|
+
def self.query(sql, args)
|
530
|
+
Db2Query::Base.query(sql, *args)
|
187
531
|
end
|
188
532
|
end
|
189
533
|
```
|
@@ -192,32 +536,36 @@ Utilize the goodness of rails model `scope`
|
|
192
536
|
```ruby
|
193
537
|
class User < Db2Record
|
194
538
|
scope :by_name, -> *args {
|
195
|
-
query(
|
196
|
-
"SELECT * FROM LIBTEST.USERS WHERE first_name = ? AND last_name = ?", {}, args
|
197
|
-
)
|
539
|
+
query("SELECT * FROM USERS WHERE $first_name = ? AND $last_name = ?", args)
|
198
540
|
}
|
199
541
|
end
|
200
542
|
```
|
543
|
+
|
201
544
|
```bash
|
202
545
|
User.by_name first_name: "Strange", last_name: "Stephen"
|
203
|
-
SQL Load (3.28ms) SELECT * FROM
|
546
|
+
SQL Load (3.28ms) SELECT * FROM USERS WHERE first_name = ? AND last_name = ? [["first_name", Strange], ["last_name", Stephen]]
|
204
547
|
=> [{:id=> 10000, :first_name=> "Strange", :last_name=> "Stephen", :email=> "strange@marvel.universe.com"}]
|
205
548
|
```
|
206
549
|
|
207
550
|
Another example:
|
208
551
|
```ruby
|
209
552
|
class User < Db2Record
|
210
|
-
scope :age_gt, ->
|
211
|
-
query("SELECT * FROM
|
553
|
+
scope :age_gt, -> *args {
|
554
|
+
query("SELECT * FROM USERS WHERE age > ?", args)
|
212
555
|
}
|
213
556
|
end
|
214
557
|
```
|
215
558
|
|
216
559
|
```bash
|
217
560
|
User.age_gt 500
|
218
|
-
SQL Load (3.28ms) SELECT * FROM
|
561
|
+
SQL Load (3.28ms) SELECT * FROM USERS WHERE age > 500
|
219
562
|
=> [{:id=> 99999, :first_name=> "Ancient", :last_name=> "One", :email=> "ancientone@marvel.universe.com"}]
|
220
563
|
```
|
221
564
|
|
222
|
-
##
|
223
|
-
|
565
|
+
## 6. Examples
|
566
|
+
|
567
|
+
For complete examples please see the basic examples [here](https://github.com/yohaneslumentut/db2_query/blob/master/test/dummy/app/queries/user_query.rb).
|
568
|
+
Please see [**Db2Session**](https://github.com/yohaneslumentut/db2_session) for **REST** and **GraphQL** implementation of multi-user on the remote server.
|
569
|
+
|
570
|
+
## 7. License
|
571
|
+
The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|