imparcial 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/imparcial/driver/{base → abstract}/expression/base.rb +11 -4
- data/lib/imparcial/driver/abstract/expression/column.rb +313 -0
- data/lib/imparcial/driver/abstract/expression/constraint.rb +149 -0
- data/lib/imparcial/driver/abstract/expression/delete.rb +88 -0
- data/lib/imparcial/driver/{base → abstract}/expression/index.rb +103 -96
- data/lib/imparcial/driver/abstract/expression/insert.rb +49 -0
- data/lib/imparcial/driver/{base → abstract}/expression/lock.rb +1 -1
- data/lib/imparcial/driver/{base/expression/util.rb → abstract/expression/record.rb} +8 -12
- data/lib/imparcial/driver/{base → abstract}/expression/select.rb +6 -4
- data/lib/imparcial/driver/{base → abstract}/expression/sequence.rb +133 -62
- data/lib/imparcial/driver/{base → abstract}/expression/statement.rb +1 -1
- data/lib/imparcial/driver/abstract/expression/table.rb +416 -0
- data/lib/imparcial/driver/abstract/expression/transaction.rb +143 -0
- data/lib/imparcial/driver/{base → abstract}/expression/update.rb +20 -3
- data/lib/imparcial/driver/abstract/expression.rb +24 -0
- data/lib/imparcial/driver/{base → abstract}/result.rb +12 -11
- data/lib/imparcial/driver/abstract/sql/column.rb +103 -0
- data/lib/imparcial/driver/abstract/sql/constraint.rb +42 -0
- data/lib/imparcial/driver/{base → abstract}/sql/delete.rb +1 -1
- data/lib/imparcial/driver/{base → abstract}/sql/index.rb +3 -11
- data/lib/imparcial/driver/{base → abstract}/sql/insert.rb +1 -1
- data/lib/imparcial/driver/abstract/sql/record.rb +19 -0
- data/lib/imparcial/driver/{base → abstract}/sql/select.rb +2 -2
- data/lib/imparcial/driver/{base → abstract}/sql/sequence.rb +2 -2
- data/lib/imparcial/driver/{base/sql/table_operation.rb → abstract/sql/table.rb} +8 -15
- data/lib/imparcial/driver/{base → abstract}/sql/transaction.rb +5 -5
- data/lib/imparcial/driver/{base → abstract}/sql/update.rb +1 -1
- data/lib/imparcial/driver/abstract/sql.rb +21 -0
- data/lib/imparcial/driver/{base → abstract}/typemap.rb +43 -89
- data/lib/imparcial/driver/{base → abstract}/util.rb +17 -5
- data/lib/imparcial/driver/abstract.rb +255 -0
- data/lib/imparcial/driver/mysql/expression/table.rb +2 -11
- data/lib/imparcial/driver/mysql/expression.rb +4 -4
- data/lib/imparcial/driver/mysql/result.rb +1 -1
- data/lib/imparcial/driver/mysql/sql/column.rb +59 -0
- data/lib/imparcial/driver/mysql/sql/constraint.rb +39 -0
- data/lib/imparcial/driver/mysql/sql/index.rb +14 -23
- data/lib/imparcial/driver/mysql/sql/sequence.rb +1 -1
- data/lib/imparcial/driver/mysql/sql/{table_metadata.rb → table.rb} +29 -5
- data/lib/imparcial/driver/mysql/sql.rb +7 -7
- data/lib/imparcial/driver/mysql/typemap.rb +1 -1
- data/lib/imparcial/driver/mysql/util.rb +1 -1
- data/lib/imparcial/driver/mysql.rb +19 -18
- data/lib/imparcial/driver/postgre/expression.rb +26 -7
- data/lib/imparcial/driver/postgre/result.rb +1 -1
- data/lib/imparcial/driver/postgre/sql/column.rb +53 -0
- data/lib/imparcial/driver/postgre/sql/constraint.rb +37 -0
- data/lib/imparcial/driver/postgre/sql/index.rb +21 -21
- data/lib/imparcial/driver/postgre/sql/sequence.rb +6 -4
- data/lib/imparcial/driver/postgre/sql/{table_metadata.rb → table.rb} +8 -8
- data/lib/imparcial/driver/postgre/sql.rb +7 -7
- data/lib/imparcial/driver/postgre/typemap.rb +2 -2
- data/lib/imparcial/driver/postgre/util.rb +1 -1
- data/lib/imparcial/driver/postgre.rb +11 -11
- data/lib/imparcial/driver.rb +1 -1
- data/lib/imparcial/exception.rb +28 -18
- data/lib/imparcial/initializer.rb +62 -0
- data/lib/imparcial.rb +1 -74
- metadata +50 -50
- data/lib/imparcial/driver/base/expression/delete.rb +0 -72
- data/lib/imparcial/driver/base/expression/insert.rb +0 -33
- data/lib/imparcial/driver/base/expression/table_diff.rb +0 -154
- data/lib/imparcial/driver/base/expression/table_evolution.rb +0 -94
- data/lib/imparcial/driver/base/expression/table_metadata.rb +0 -122
- data/lib/imparcial/driver/base/expression/table_operation.rb +0 -137
- data/lib/imparcial/driver/base/expression/transaction.rb +0 -59
- data/lib/imparcial/driver/base/expression.rb +0 -37
- data/lib/imparcial/driver/base/sql/table_metadata.rb +0 -29
- data/lib/imparcial/driver/base/sql.rb +0 -25
- data/lib/imparcial/driver/base.rb +0 -156
- data/lib/imparcial/driver/mysql/expression/index.rb +0 -44
- data/lib/imparcial/driver/mysql/sql/table_operation.rb +0 -20
- data/lib/imparcial/driver/postgre/expression/index.rb +0 -10
- data/lib/imparcial/driver/postgre/expression/sequence.rb +0 -9
- data/lib/imparcial/driver/postgre/expression/table.rb +0 -20
- data/lib/imparcial/driver/postgre/sql/table_operation.rb +0 -9
@@ -1,137 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module ExpressionBase
|
4
|
-
module TableOperation
|
5
|
-
|
6
|
-
private
|
7
|
-
|
8
|
-
def expected_options_for_creating_table
|
9
|
-
|
10
|
-
{:table_name => :required, :fields => :required}
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def expected_options_for_dropping_table
|
15
|
-
|
16
|
-
{:table_name => :required}
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
###########################################
|
21
|
-
# #
|
22
|
-
# Dropping Table #
|
23
|
-
# #
|
24
|
-
###########################################
|
25
|
-
|
26
|
-
public
|
27
|
-
|
28
|
-
# Drop a table by table_name.
|
29
|
-
# An exception will be raised if nothing is found.
|
30
|
-
# Usage:
|
31
|
-
# drop_table :table_name => 'something'
|
32
|
-
|
33
|
-
def drop_table ( options = {} )
|
34
|
-
|
35
|
-
check_options expected_options_for_dropping_table, options
|
36
|
-
|
37
|
-
query sql_for_dropping_table( options )
|
38
|
-
|
39
|
-
rescue adapter_specific_exception => ex
|
40
|
-
|
41
|
-
raise TableDropError.new(ex.message)
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
# Drop a table by name.
|
46
|
-
# An exception shall not be raised independently of what happens.
|
47
|
-
|
48
|
-
def drop_table_if_necessary ( options = {} )
|
49
|
-
|
50
|
-
drop_table options
|
51
|
-
|
52
|
-
rescue TableDropError; end
|
53
|
-
|
54
|
-
# Drop all tables.
|
55
|
-
|
56
|
-
def drop_all_tables
|
57
|
-
|
58
|
-
for table_name in retrieve_tables
|
59
|
-
|
60
|
-
query sql_for_dropping_table( {:table_name => table_name} )
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
rescue adapter_specific_exception => ex
|
65
|
-
|
66
|
-
raise TableDropError.new(ex.message)
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
###########################################
|
71
|
-
# #
|
72
|
-
# Creating Table #
|
73
|
-
# #
|
74
|
-
###########################################
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def column_format
|
79
|
-
|
80
|
-
[:name, :type, :size, :allow_null, :auto_increment, :default_value]
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
def build_column ( field )
|
85
|
-
|
86
|
-
raise OptionError unless field
|
87
|
-
|
88
|
-
# Fill it up with default values if needed.
|
89
|
-
|
90
|
-
parse_field field
|
91
|
-
sql_column = field_to_column field
|
92
|
-
|
93
|
-
syntax = ''
|
94
|
-
|
95
|
-
for attr in column_format
|
96
|
-
|
97
|
-
syntax += sql_column[attr] + ' ' if sql_column[attr]
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
syntax
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def field_to_column ( field )
|
106
|
-
|
107
|
-
column = {}
|
108
|
-
column[:name] = quote(field[:name])
|
109
|
-
column[:type] = regular_types[field[:type]]
|
110
|
-
column[:size] = '(' + quote_value(field[:size]) + ')' if field[:size]
|
111
|
-
column[:allow_null] = field[:allow_null] == true ? 'NULL' : 'NOT NULL'
|
112
|
-
column[:default_value] = field[:default_value] != nil ? ' DEFAULT ' + quote_value(field[:default_value]) : ''
|
113
|
-
|
114
|
-
column
|
115
|
-
|
116
|
-
end
|
117
|
-
|
118
|
-
public
|
119
|
-
|
120
|
-
# Create a table.
|
121
|
-
|
122
|
-
def create_table ( options = {} )
|
123
|
-
|
124
|
-
check_options expected_options_for_creating_table, options
|
125
|
-
|
126
|
-
query sql_for_creating_table( options )
|
127
|
-
|
128
|
-
rescue adapter_specific_exception => ex
|
129
|
-
|
130
|
-
raise TableCreateError.new(ex.message)
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module ExpressionBase
|
4
|
-
module Transaction
|
5
|
-
|
6
|
-
public
|
7
|
-
|
8
|
-
# Create a savepoint for a transaction.
|
9
|
-
|
10
|
-
def create_savepoint ( name )
|
11
|
-
|
12
|
-
query sql_for_saving_point( name )
|
13
|
-
|
14
|
-
rescue adapter_specific_exception => ex
|
15
|
-
|
16
|
-
raise TransactionError.new(ex.message)
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
# Rollback a transaction to the specified savepoint.
|
21
|
-
|
22
|
-
def restore_savepoint ( name )
|
23
|
-
|
24
|
-
query sql_for_rolling_back( name )
|
25
|
-
|
26
|
-
rescue adapter_specific_exception => ex
|
27
|
-
|
28
|
-
raise TransactionError.new(ex.message)
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
# Initialize a transaction.
|
33
|
-
|
34
|
-
def initialize_transaction
|
35
|
-
|
36
|
-
query sql_for_initializing_a_transaction
|
37
|
-
|
38
|
-
rescue adapter_specific_exception => ex
|
39
|
-
|
40
|
-
raise TransactionError.new(ex.message)
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
# Finalize a transaction.
|
45
|
-
|
46
|
-
def terminate_transaction
|
47
|
-
|
48
|
-
query sql_for_terminating_a_transaction
|
49
|
-
|
50
|
-
rescue adapter_specific_exception => ex
|
51
|
-
|
52
|
-
raise TransactionError.new(ex.message)
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'imparcial/driver/base/expression/base'
|
2
|
-
require 'imparcial/driver/base/expression/statement'
|
3
|
-
require 'imparcial/driver/base/expression/sequence'
|
4
|
-
require 'imparcial/driver/base/expression/table_diff'
|
5
|
-
require 'imparcial/driver/base/expression/table_operation'
|
6
|
-
require 'imparcial/driver/base/expression/table_metadata'
|
7
|
-
require 'imparcial/driver/base/expression/table_evolution'
|
8
|
-
require 'imparcial/driver/base/expression/insert'
|
9
|
-
require 'imparcial/driver/base/expression/select'
|
10
|
-
require 'imparcial/driver/base/expression/delete'
|
11
|
-
require 'imparcial/driver/base/expression/update'
|
12
|
-
require 'imparcial/driver/base/expression/util'
|
13
|
-
require 'imparcial/driver/base/expression/lock'
|
14
|
-
require 'imparcial/driver/base/expression/transaction'
|
15
|
-
require 'imparcial/driver/base/expression/index'
|
16
|
-
|
17
|
-
module Imparcial
|
18
|
-
module Driver
|
19
|
-
module ExpressionBase
|
20
|
-
include Base
|
21
|
-
include Statement
|
22
|
-
include Sequence
|
23
|
-
include TableDiff
|
24
|
-
include TableOperation
|
25
|
-
include TableMetadata
|
26
|
-
include TableEvolution
|
27
|
-
include Insert
|
28
|
-
include Select
|
29
|
-
include Delete
|
30
|
-
include Update
|
31
|
-
include Util
|
32
|
-
include Lock
|
33
|
-
include Transaction
|
34
|
-
include Index
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module SQLBase
|
4
|
-
module TableMetadata
|
5
|
-
|
6
|
-
private
|
7
|
-
|
8
|
-
# There's no standard way to retrieve tables.
|
9
|
-
# Subclasses need to override this method.
|
10
|
-
|
11
|
-
def sql_for_retrieving_tables
|
12
|
-
|
13
|
-
raise FeatureNotFound
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
# There's no standard way to retrieve columns.
|
18
|
-
# Subclasses need to override this method.
|
19
|
-
|
20
|
-
def sql_for_retrieving_columns ( options = {} )
|
21
|
-
|
22
|
-
raise FeatureNotFound
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'imparcial/driver/base/sql/table_operation'
|
2
|
-
require 'imparcial/driver/base/sql/table_metadata'
|
3
|
-
require 'imparcial/driver/base/sql/sequence'
|
4
|
-
require 'imparcial/driver/base/sql/transaction'
|
5
|
-
require 'imparcial/driver/base/sql/index'
|
6
|
-
require 'imparcial/driver/base/sql/insert'
|
7
|
-
require 'imparcial/driver/base/sql/delete'
|
8
|
-
require 'imparcial/driver/base/sql/select'
|
9
|
-
require 'imparcial/driver/base/sql/update'
|
10
|
-
|
11
|
-
module Imparcial
|
12
|
-
module Driver
|
13
|
-
module SQLBase
|
14
|
-
include TableOperation
|
15
|
-
include TableMetadata
|
16
|
-
include Sequence
|
17
|
-
include Transaction
|
18
|
-
include Index
|
19
|
-
include Insert
|
20
|
-
include Delete
|
21
|
-
include Select
|
22
|
-
include Update
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
require 'imparcial/driver/base/util'
|
2
|
-
require 'imparcial/driver/base/typemap'
|
3
|
-
require 'imparcial/driver/base/result'
|
4
|
-
require 'imparcial/driver/base/sql'
|
5
|
-
require 'imparcial/driver/base/expression'
|
6
|
-
|
7
|
-
module Imparcial
|
8
|
-
module Driver
|
9
|
-
|
10
|
-
# You have seriously to pay attention on this class.
|
11
|
-
# Basically speaking, it servers as base for all new drivers.
|
12
|
-
# The idea behind Imparcial is to keep an abstract interface without
|
13
|
-
# exposing any database-specific-features.
|
14
|
-
# Of course, it's an impossible mission anyhow. There are so many
|
15
|
-
# databases out there and there are so many cool features that
|
16
|
-
# make impossible not to expose them.
|
17
|
-
# We recommend you to obey the interface as much as possible.
|
18
|
-
# However you may develop some specific features.
|
19
|
-
|
20
|
-
class AdapterBase
|
21
|
-
|
22
|
-
# Include SQL syntax generation.
|
23
|
-
# Here where all SQL syntax lies on.
|
24
|
-
|
25
|
-
include SQLBase
|
26
|
-
|
27
|
-
# Include the expression mechanism.
|
28
|
-
# Things like create table, drop table, select and so forth.
|
29
|
-
# They all come from this module.
|
30
|
-
|
31
|
-
include ExpressionBase
|
32
|
-
|
33
|
-
# Include some util methods.
|
34
|
-
# Things like quoting columns, tables and values.
|
35
|
-
|
36
|
-
include UtilBase
|
37
|
-
|
38
|
-
# Include Typemap mechanism.
|
39
|
-
|
40
|
-
include TypemapBase
|
41
|
-
|
42
|
-
# Let's keep a track of the initializer. We'll need some info eventually.
|
43
|
-
|
44
|
-
attr_reader :initializer
|
45
|
-
|
46
|
-
# Let's also keep a track of the connection.
|
47
|
-
|
48
|
-
attr_accessor :conn
|
49
|
-
alias_method :connection, :conn
|
50
|
-
|
51
|
-
attr_reader :host, :user, :pass, :database, :socket, :port
|
52
|
-
|
53
|
-
def initialize ( initializer )
|
54
|
-
|
55
|
-
# Cannot accept classes different from Initializer.
|
56
|
-
|
57
|
-
if initializer.class != Imparcial::Initializer
|
58
|
-
|
59
|
-
msg = 'An initializer is needed in order to start a driver'
|
60
|
-
raise AdapterConfigError.new(msg)
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
@initializer = initializer
|
65
|
-
|
66
|
-
@host = initializer.host
|
67
|
-
|
68
|
-
if not initializer.user
|
69
|
-
|
70
|
-
raise AdapterConfigError.new('Adapter requires an username')
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
@user = initializer.user
|
75
|
-
|
76
|
-
if not initializer.pass
|
77
|
-
|
78
|
-
raise AdapterConfigError.new('Adapter requires a password')
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
@pass = initializer.pass
|
83
|
-
|
84
|
-
if not initializer.database
|
85
|
-
|
86
|
-
raise AdapterConfigErrornew('Adapter requires a database')
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
@database = initializer.database
|
91
|
-
@socket = initializer.socket
|
92
|
-
@port = initializer.port
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
#####################################################
|
97
|
-
# #
|
98
|
-
# Those are methods shared among all databases. #
|
99
|
-
# Hardly, one will need to override them. #
|
100
|
-
# #
|
101
|
-
#####################################################
|
102
|
-
|
103
|
-
def conn
|
104
|
-
|
105
|
-
raise AdapterConnectionError.new('Have you connected?') unless @conn
|
106
|
-
@conn
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
def close
|
111
|
-
|
112
|
-
conn.close
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
def result
|
117
|
-
|
118
|
-
raise ResultError.new('Have you made a query?') unless @result
|
119
|
-
@result
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
#####################################################
|
124
|
-
# #
|
125
|
-
# Subclasses must override those following methods. #
|
126
|
-
# They are driver-specific. #
|
127
|
-
# #
|
128
|
-
#####################################################
|
129
|
-
|
130
|
-
def connect
|
131
|
-
|
132
|
-
raise FeatureNotFound
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
def version
|
137
|
-
|
138
|
-
raise FeatureNotFound
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
def query ( sql )
|
143
|
-
|
144
|
-
raise FeatureNotFound
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
def adapter_specific_exception
|
149
|
-
|
150
|
-
raise FeatureNotFound
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module ExpressionMysql
|
4
|
-
module Index
|
5
|
-
|
6
|
-
public
|
7
|
-
|
8
|
-
def drop_all_indexes
|
9
|
-
|
10
|
-
for index in retrieve_indexes
|
11
|
-
|
12
|
-
meta = {:index_name => index[:name], :table_name => index[:table]}
|
13
|
-
query sql_for_dropping_index(meta)
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
rescue adapter_specific_exception => ex
|
18
|
-
|
19
|
-
raise IndexDropError.new(ex.message)
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def drop_all_indexes_for_table ( options = {} )
|
24
|
-
|
25
|
-
check_options expected_options_for_index_table, options
|
26
|
-
|
27
|
-
for index in retrieve_indexes_for_table options
|
28
|
-
|
29
|
-
meta = {:index_name => index[:name], :table_name => options[:table_name]}
|
30
|
-
query sql_for_dropping_index(meta)
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
rescue adapter_specific_exception => ex
|
35
|
-
|
36
|
-
raise IndexDropError.new(ex.message)
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module SQLMysql
|
4
|
-
module TableOperation
|
5
|
-
|
6
|
-
private
|
7
|
-
|
8
|
-
def sql_for_creating_table ( options = {} )
|
9
|
-
|
10
|
-
syntax = super
|
11
|
-
syntax += 'ENGINE = ' + options[:engine].to_s if options[:engine]
|
12
|
-
|
13
|
-
syntax
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Imparcial
|
2
|
-
module Driver
|
3
|
-
module ExpressionPostgre
|
4
|
-
module Table
|
5
|
-
|
6
|
-
private
|
7
|
-
|
8
|
-
def field_to_column ( field )
|
9
|
-
|
10
|
-
column = super
|
11
|
-
column[:type] = 'SERIAL' if field[:auto_increment]
|
12
|
-
|
13
|
-
column
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|