rom-sql 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +12 -7
  4. data/CHANGELOG.md +28 -0
  5. data/Gemfile +6 -9
  6. data/README.md +5 -4
  7. data/circle.yml +10 -0
  8. data/lib/rom/plugins/relation/sql/auto_combine.rb +16 -3
  9. data/lib/rom/plugins/relation/sql/auto_wrap.rb +3 -2
  10. data/lib/rom/sql/association.rb +75 -0
  11. data/lib/rom/sql/association/many_to_many.rb +86 -0
  12. data/lib/rom/sql/association/many_to_one.rb +60 -0
  13. data/lib/rom/sql/association/name.rb +70 -0
  14. data/lib/rom/sql/association/one_to_many.rb +9 -0
  15. data/lib/rom/sql/association/one_to_one.rb +46 -0
  16. data/lib/rom/sql/association/one_to_one_through.rb +9 -0
  17. data/lib/rom/sql/commands.rb +2 -0
  18. data/lib/rom/sql/commands/create.rb +2 -2
  19. data/lib/rom/sql/commands/delete.rb +0 -1
  20. data/lib/rom/sql/commands/postgres.rb +76 -0
  21. data/lib/rom/sql/commands/update.rb +6 -3
  22. data/lib/rom/sql/commands_ext/postgres.rb +17 -0
  23. data/lib/rom/sql/gateway.rb +23 -15
  24. data/lib/rom/sql/header.rb +7 -1
  25. data/lib/rom/sql/plugin/assoc_macros.rb +3 -3
  26. data/lib/rom/sql/plugin/associates.rb +50 -9
  27. data/lib/rom/sql/qualified_attribute.rb +53 -0
  28. data/lib/rom/sql/relation.rb +76 -25
  29. data/lib/rom/sql/relation/reading.rb +138 -35
  30. data/lib/rom/sql/relation/writing.rb +21 -0
  31. data/lib/rom/sql/schema.rb +35 -0
  32. data/lib/rom/sql/schema/associations_dsl.rb +68 -0
  33. data/lib/rom/sql/schema/dsl.rb +27 -0
  34. data/lib/rom/sql/schema/inferrer.rb +80 -0
  35. data/lib/rom/sql/support/active_support_notifications.rb +27 -17
  36. data/lib/rom/sql/types.rb +11 -0
  37. data/lib/rom/sql/types/pg.rb +26 -0
  38. data/lib/rom/sql/version.rb +1 -1
  39. data/rom-sql.gemspec +4 -2
  40. data/spec/integration/association/many_to_many_spec.rb +137 -0
  41. data/spec/integration/association/many_to_one_spec.rb +110 -0
  42. data/spec/integration/association/one_to_many_spec.rb +58 -0
  43. data/spec/integration/association/one_to_one_spec.rb +57 -0
  44. data/spec/integration/association/one_to_one_through_spec.rb +90 -0
  45. data/spec/integration/combine_spec.rb +24 -24
  46. data/spec/integration/commands/create_spec.rb +215 -168
  47. data/spec/integration/commands/delete_spec.rb +88 -46
  48. data/spec/integration/commands/update_spec.rb +141 -60
  49. data/spec/integration/commands/upsert_spec.rb +83 -0
  50. data/spec/integration/gateway_spec.rb +9 -17
  51. data/spec/integration/migration_spec.rb +3 -5
  52. data/spec/integration/plugins/associates_spec.rb +168 -0
  53. data/spec/integration/plugins/auto_wrap_spec.rb +46 -0
  54. data/spec/integration/read_spec.rb +80 -77
  55. data/spec/integration/relation_schema_spec.rb +180 -0
  56. data/spec/integration/schema_inference_spec.rb +67 -0
  57. data/spec/integration/setup_spec.rb +22 -0
  58. data/spec/{support → integration/support}/active_support_notifications_spec.rb +0 -0
  59. data/spec/{support → integration/support}/rails_log_subscriber_spec.rb +0 -0
  60. data/spec/shared/database_setup.rb +46 -8
  61. data/spec/shared/relations.rb +8 -0
  62. data/spec/shared/users_and_accounts.rb +10 -0
  63. data/spec/shared/users_and_tasks.rb +20 -2
  64. data/spec/spec_helper.rb +64 -11
  65. data/spec/support/helpers.rb +9 -0
  66. data/spec/unit/association/many_to_many_spec.rb +89 -0
  67. data/spec/unit/association/many_to_one_spec.rb +81 -0
  68. data/spec/unit/association/name_spec.rb +68 -0
  69. data/spec/unit/association/one_to_many_spec.rb +62 -0
  70. data/spec/unit/association/one_to_one_spec.rb +62 -0
  71. data/spec/unit/association/one_to_one_through_spec.rb +69 -0
  72. data/spec/unit/association_errors_spec.rb +2 -4
  73. data/spec/unit/gateway_spec.rb +12 -3
  74. data/spec/unit/migration_tasks_spec.rb +3 -3
  75. data/spec/unit/migrator_spec.rb +2 -4
  76. data/spec/unit/{combined_associations_spec.rb → plugin/assoc_macros/combined_associations_spec.rb} +13 -19
  77. data/spec/unit/{many_to_many_spec.rb → plugin/assoc_macros/many_to_many_spec.rb} +9 -15
  78. data/spec/unit/{many_to_one_spec.rb → plugin/assoc_macros/many_to_one_spec.rb} +9 -14
  79. data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +78 -0
  80. data/spec/unit/plugin/base_view_spec.rb +11 -11
  81. data/spec/unit/plugin/pagination_spec.rb +62 -62
  82. data/spec/unit/relation_spec.rb +218 -146
  83. data/spec/unit/schema_spec.rb +15 -14
  84. data/spec/unit/types_spec.rb +40 -0
  85. metadata +105 -21
  86. data/.rubocop.yml +0 -74
  87. data/.rubocop_todo.yml +0 -21
  88. data/spec/unit/one_to_many_spec.rb +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 017ad7f1a32c576596913c9f86d7d5781efad7e6
4
- data.tar.gz: ee55e2a05c6733c1b10dbedad0a638c7d0da066d
3
+ metadata.gz: 5da70a1471f9c0b3a27f10cacf6af54f060090be
4
+ data.tar.gz: 1f391fdbd269bbf0df9d0af23a5fc112948ce1f2
5
5
  SHA512:
6
- metadata.gz: 1675cade8ac1b92697582e6cfd0ccd17bc3af848a2677a0fbab6e39d188d660e16b937d7d2643ffbd9d94fca60bcdb9af758255abde5af9bc7c0c3298f78790f
7
- data.tar.gz: c9bc786e8e52a805453bca55cfb3f6ca236761455d5f68d598bc476463282d8c74d4d383343693d3a1e4abefec5e444bef6cd06a4319f312c61630af33443805
6
+ metadata.gz: f91c5854d45dc52b26ddb0eaca6dc465ff02db69bd145d6ce010c6eaa93571ac46842d68a6897e0b88fffb7639efe26c94435a035c0d4a0790be37defd846fa2
7
+ data.tar.gz: 879cf6a660eb79ab84b1adbfb33ede96613d786356457182d81bc3f00f2a7165fbe939d25b7bd0755242710469cffae650976252c9c19eeda37067a89a0407ec
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
2
  --order random
3
+ --require ./spec/spec_helper
data/.travis.yml CHANGED
@@ -1,28 +1,33 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
+ services:
5
+ - postgresql
6
+ - mysql
4
7
  bundler_args: --without yard guard benchmarks tools
5
8
  before_script:
6
9
  - psql -c 'create database rom_sql;' -U postgres
10
+ - mysql -e 'create database rom_sql;'
7
11
  script: "bundle exec rake ci"
8
12
  rvm:
9
- - 2.0
10
- - 2.1
11
- - 2.2
12
- - 2.3.0
13
+ - 2.1.9
14
+ - 2.2.5
15
+ - 2.3.1
13
16
  - rbx-2
14
- - jruby-9000
15
- - jruby-head
17
+ - jruby-9.0.5.0
16
18
  - ruby-head
17
19
  env:
18
20
  global:
19
21
  - CODECLIMATE_REPO_TOKEN=03d7f66589572702b12426d2bc71c4de6281a96139e33b335b894264b1f8f0b0
20
22
  - JRUBY_OPTS='--dev -J-Xmx1024M'
23
+ - PG_LTE_95='false'
21
24
  matrix:
22
25
  allow_failures:
23
- - rvm: jruby-9000
24
26
  - rvm: ruby-head
25
27
  - rvm: jruby-head
28
+ include:
29
+ - rvm: jruby-head
30
+ before_install: gem install bundler --no-ri --no-rdoc
26
31
  notifications:
27
32
  webhooks:
28
33
  urls:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## v0.8.0 2016-07-27
2
+
3
+ ### Added
4
+
5
+ * Support for relation schemas with SQL-specific data types (solnic + flash-gordon)
6
+ * One-To-Many support in schemas (solnic + flash-gordon)
7
+ * One-To-One support in schemas (solnic + flash-gordon)
8
+ * One-To-One-Through support in schemas (solnic + flash-gordon)
9
+ * Many-To-One support in schemas (solnic + flash-gordon)
10
+ * Many-To-Many support in schemas (solnic + flash-gordon)
11
+ * Support for `has_many`, `has_one` and `belongs_to` convenient methods in schema DSL (solnic)
12
+ * Support for custom PG types: `Types::PG::Array`, `Types::PG::Hash`, `Types::PG::JSON`, and `Types::PG::Bytea` (solnic + flash-gordon)
13
+ * Optional automatic schema inference for attributes and foreign keys based on DB metadata provided by Sequel (flash-gordon)
14
+ * Support for arbitrary dataset and FK names in schemas (flash-gordon)
15
+ * Support for native upserts in PostgreSQL >= 9.5 via `Commands::Postgres::Upsert` (gotar + flash-gordon)
16
+
17
+ ### Changed
18
+
19
+ * `Create` and `Update` commands have `:schema` plugin enabled by default which sets input handler based on schema definition automatically (solnic)
20
+ * `associates` command plugin uses schema associations now (solnic)
21
+ * Dropped MRI 2.0.x support
22
+
23
+ ### Fixed
24
+
25
+ * `Create` command properly materialize result when `:one` is set (AMHOL)
26
+
27
+ [Compare v0.7.0...v0.8.0](https://github.com/rom-rb/rom-sql/compare/v0.7.0...v0.8.0)
28
+
1
29
  ## v0.7.0 2016-01-06
2
30
 
3
31
  ### Added
data/Gemfile CHANGED
@@ -6,16 +6,13 @@ group :test do
6
6
  gem 'byebug', platforms: :mri
7
7
  gem 'anima', '~> 0.2.0'
8
8
  gem 'virtus'
9
- gem 'activesupport'
9
+ gem 'activesupport', '~> 4.2'
10
10
  gem 'rspec', '~> 3.1'
11
11
  gem 'codeclimate-test-reporter', require: false
12
12
  gem 'pg', platforms: [:mri, :rbx]
13
- gem 'pg_jruby', platforms: :jruby
14
- end
15
-
16
- group :tools do
17
- gem 'guard'
18
- gem 'guard-rspec'
19
- gem 'guard-rubocop'
20
- gem 'rubocop', '~> 0.28'
13
+ gem 'mysql2', platforms: [:mri, :rbx]
14
+ gem 'jdbc-postgres', platforms: :jruby
15
+ gem 'jdbc-mysql', platforms: :jruby
16
+ gem 'sqlite3', platforms: [:mri, :rbx]
17
+ gem 'jdbc-sqlite3', platforms: :jruby
21
18
  end
data/README.md CHANGED
@@ -4,11 +4,11 @@
4
4
  [codeclimate]: https://codeclimate.com/github/rom-rb/rom-sql
5
5
  [inchpages]: http://inch-ci.org/github/rom-rb/rom-sql
6
6
 
7
- # ROM::SQL
7
+ # rom-sql
8
8
 
9
9
  [![Gem Version](https://badge.fury.io/rb/rom-sql.svg)][gem]
10
10
  [![Build Status](https://travis-ci.org/rom-rb/rom-sql.svg?branch=master)][travis]
11
- [![Dependency Status](https://gemnasium.com/rom-rb/rom-sql.png)][gemnasium]
11
+ [![Dependency Status](https://gemnasium.com/rom-rb/rom-sql.svg)][gemnasium]
12
12
  [![Code Climate](https://codeclimate.com/github/rom-rb/rom-sql/badges/gpa.svg)][codeclimate]
13
13
  [![Test Coverage](https://codeclimate.com/github/rom-rb/rom-sql/badges/coverage.svg)][codeclimate]
14
14
  [![Inline docs](http://inch-ci.org/github/rom-rb/rom-sql.svg?branch=master)][inchpages]
@@ -17,7 +17,8 @@ RDBMS support for [Ruby Object Mapper](https://github.com/rom-rb/rom).
17
17
 
18
18
  Resources:
19
19
 
20
- - [Guides](http://rom-rb.org/guides/adapters/sql/)
20
+ - [User Documentation](http://rom-rb.org/learn/sql/)
21
+ - [API Documentation](http://rubydoc.info/gems/rom-sql)
21
22
 
22
23
  ## Installation
23
24
 
@@ -35,7 +36,7 @@ Or install it yourself as:
35
36
 
36
37
  $ gem install rom-sql
37
38
 
38
- Check out [SQL guide](http://rom-rb.org/guides/adapters/sql/) for usage examples.
39
+ Check out [SQL guide](http://rom-rb.org/learn/adapters/sql/) for usage examples.
39
40
 
40
41
  ## ROADMAP
41
42
 
data/circle.yml ADDED
@@ -0,0 +1,10 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.3.1
4
+ dependencies:
5
+ bundler:
6
+ without: [yard guard benchmarks tools]
7
+ database:
8
+ override:
9
+ - psql -c 'create database rom_sql;' -U postgres
10
+ - mysql -e 'create database rom_sql;'
@@ -16,6 +16,7 @@ module ROM
16
16
  def inherited(klass)
17
17
  super
18
18
  klass.auto_curry :for_combine
19
+ klass.auto_curry :preload
19
20
  end
20
21
  end
21
22
 
@@ -27,9 +28,21 @@ module ROM
27
28
  # @return [SQL::Relation]
28
29
  #
29
30
  # @api private
30
- def for_combine(keys, relation)
31
- pk, fk = keys.to_a.flatten
32
- where(fk => relation.map { |tuple| tuple[pk] })
31
+ def for_combine(spec)
32
+ source_key, target_key, target =
33
+ case spec
34
+ when ROM::SQL::Association
35
+ [*spec.join_keys(__registry__).flatten, spec.call(__registry__)]
36
+ else
37
+ [*spec.flatten, self]
38
+ end
39
+
40
+ target.preload(source_key, target_key)
41
+ end
42
+
43
+ # @api private
44
+ def preload(source_key, target_key, source)
45
+ where(target_key => source.pluck(source_key.to_sym))
33
46
  end
34
47
  end
35
48
  end
@@ -29,10 +29,11 @@ module ROM
29
29
  # @api private
30
30
  def for_wrap(keys, name)
31
31
  other = __registry__[name]
32
+ other_dataset = other.name.dataset
32
33
 
33
- inner_join(name, keys)
34
+ inner_join(other_dataset, keys)
34
35
  .select(*qualified.header.columns)
35
- .select_append(*other.prefix(other.name).qualified.header)
36
+ .select_append(*other.prefix(other_dataset).qualified.header)
36
37
  end
37
38
  end
38
39
  end
@@ -0,0 +1,75 @@
1
+ require 'rom/support/constants'
2
+ require 'rom/sql/qualified_attribute'
3
+ require 'rom/sql/association/name'
4
+
5
+ module ROM
6
+ module SQL
7
+ # Abstract association class
8
+ #
9
+ # @api public
10
+ class Association
11
+ include Dry::Equalizer(:source, :target, :result)
12
+ include Options
13
+ extend ClassMacros
14
+
15
+ defines :result
16
+
17
+ # @!attribute [r] source
18
+ # @return [ROM::Relation::Name] the source relation name
19
+ attr_reader :source
20
+
21
+ # @!attribute [r] target
22
+ # @return [ROM::Relation::Name] the target relation name
23
+ attr_reader :target
24
+
25
+ # @!attribute [r] relation
26
+ # @return [Symbol] an optional relation identifier for the target
27
+ option :relation, accepts: [Symbol], reader: true
28
+
29
+ # @!attribute [r] result
30
+ # @return [Symbol] either :one or :many
31
+ option :result, accepts: [Symbol], reader: true, default: -> assoc { assoc.class.result }
32
+
33
+ # @!attribute [r] as
34
+ # @return [Symbol] an optional association alias name
35
+ option :as, accepts: [Symbol], reader: true, default: -> assoc { assoc.target.to_sym }
36
+
37
+ alias_method :name, :as
38
+
39
+ # @api private
40
+ def initialize(source, target, options = EMPTY_HASH)
41
+ @source = Name[source]
42
+ @target = Name[options[:relation] || target, target, options[:as] || target]
43
+ super
44
+ end
45
+
46
+ # Returns a qualified attribute name for a given dataset
47
+ #
48
+ # This is compatible with Sequel's SQL generator and can be used in query
49
+ # DSL methods
50
+ #
51
+ # @param name [ROM::Relation::Name]
52
+ # @param attribute [Symbol]
53
+ #
54
+ # @return [QualifiedAttribute]
55
+ #
56
+ # @api public
57
+ def qualify(name, attribute)
58
+ QualifiedAttribute[name.dataset, attribute]
59
+ end
60
+
61
+ protected
62
+
63
+ # @api private
64
+ def join_key_map(relations)
65
+ join_keys(relations).to_a.flatten.map(&:to_sym)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ require 'rom/sql/association/one_to_one'
72
+ require 'rom/sql/association/one_to_many'
73
+ require 'rom/sql/association/many_to_many'
74
+ require 'rom/sql/association/many_to_one'
75
+ require 'rom/sql/association/one_to_one_through'
@@ -0,0 +1,86 @@
1
+ module ROM
2
+ module SQL
3
+ class Association
4
+ class ManyToMany < Association
5
+ attr_reader :through
6
+
7
+ result :many
8
+
9
+ option :through, default: nil, type: Symbol
10
+
11
+ # @api private
12
+ def initialize(*)
13
+ super
14
+ @through = Relation::Name[
15
+ options[:through] || options[:through_relation], options[:through]
16
+ ]
17
+ end
18
+
19
+ # @api public
20
+ def call(relations)
21
+ join_rel = join_relation(relations)
22
+ assocs = join_rel.associations
23
+
24
+ left = assocs[target].call(relations)
25
+ right = relations[target.relation]
26
+
27
+ left_fk = join_rel.foreign_key(source.relation)
28
+
29
+ columns = right.header.exclude(left_fk).qualified.to_a
30
+ columns << left_fk unless right.header.names.include?(left_fk)
31
+
32
+ relation = left
33
+ .inner_join(source, join_keys(relations))
34
+ .select(*columns)
35
+ .order(*right.header.project(*right.primary_key).qualified)
36
+
37
+ relation.with(attributes: relation.header.names)
38
+ end
39
+
40
+ # @api public
41
+ def join_keys(relations)
42
+ with_keys(relations) { |source_key, target_key|
43
+ { qualify(source, source_key) => qualify(through, target_key) }
44
+ }
45
+ end
46
+
47
+ # @api public
48
+ def combine_keys(relations)
49
+ Hash[*with_keys(relations)]
50
+ end
51
+
52
+ # @api private
53
+ def associate(relations, children, parent)
54
+ ((spk, sfk), (tfk, tpk)) = join_key_map(relations)
55
+
56
+ children.map { |tuple|
57
+ { sfk => tuple.fetch(spk), tfk => parent.fetch(tpk) }
58
+ }
59
+ end
60
+
61
+ # @api private
62
+ def join_relation(relations)
63
+ relations[through.relation]
64
+ end
65
+
66
+ protected
67
+
68
+ # @api private
69
+ def with_keys(relations, &block)
70
+ source_key = relations[source.relation].primary_key
71
+ target_key = relations[through.relation].foreign_key(source.relation)
72
+ return [source_key, target_key] unless block
73
+ yield(source_key, target_key)
74
+ end
75
+
76
+ # @api private
77
+ def join_key_map(relations)
78
+ left = super
79
+ right = join_relation(relations).associations[target].join_key_map(relations)
80
+
81
+ [left, right]
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,60 @@
1
+ module ROM
2
+ module SQL
3
+ class Association
4
+ class ManyToOne < Association
5
+ result :one
6
+
7
+ # @api public
8
+ def call(relations)
9
+ left = relations[target.relation]
10
+ right = relations[source.relation]
11
+
12
+ right_pk = right.schema.primary_key.map { |a| a.meta[:name] }
13
+ right_fk = right.foreign_key(target.relation)
14
+
15
+ pk_to_fk = Hash[right_pk.product(Array(left.foreign_key(source.relation)))]
16
+
17
+ columns = left.header.qualified.to_a + right.header
18
+ .project(*right_pk)
19
+ .rename(pk_to_fk)
20
+ .qualified.to_a
21
+
22
+ relation = left
23
+ .inner_join(source, right_fk => left.primary_key)
24
+ .select(*columns)
25
+ .order(*right.header.project(*right.primary_key).qualified)
26
+
27
+ relation.with(attributes: relation.header.names)
28
+ end
29
+
30
+ # @api public
31
+ def combine_keys(relations)
32
+ Hash[*with_keys(relations)]
33
+ end
34
+
35
+ # @api public
36
+ def join_keys(relations)
37
+ with_keys(relations) { |source_key, target_key|
38
+ { qualify(source, source_key) => qualify(target, target_key) }
39
+ }
40
+ end
41
+
42
+ # @api private
43
+ def associate(relations, child, parent)
44
+ fk, pk = join_key_map(relations)
45
+ child.merge(fk => parent.fetch(pk))
46
+ end
47
+
48
+ protected
49
+
50
+ # @api private
51
+ def with_keys(relations, &block)
52
+ source_key = relations[source.relation].foreign_key(target.relation)
53
+ target_key = relations[target.relation].primary_key
54
+ return [source_key, target_key] unless block
55
+ yield(source_key, target_key)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,70 @@
1
+ require 'dry/equalizer'
2
+ require 'rom/relation/name'
3
+ require 'rom/support/cache'
4
+
5
+ module ROM
6
+ module SQL
7
+ class Association
8
+ class Name
9
+ include Dry::Equalizer.new(:relation_name, :key)
10
+
11
+ extend Cache
12
+
13
+ attr_reader :relation_name
14
+
15
+ attr_reader :key
16
+
17
+ alias_method :to_sym, :key
18
+
19
+ def self.[](*args)
20
+ fetch_or_store(args) do
21
+ rel, ds, aliaz = args
22
+
23
+ if rel.is_a?(ROM::Relation::Name)
24
+ new(rel, rel.dataset)
25
+ elsif rel.is_a?(self)
26
+ rel
27
+ elsif aliaz
28
+ new(ROM::Relation::Name[rel, ds], aliaz)
29
+ elsif ds.nil?
30
+ new(ROM::Relation::Name[rel], rel)
31
+ else
32
+ new(ROM::Relation::Name[rel, ds], ds)
33
+ end
34
+ end
35
+ end
36
+
37
+ def initialize(relation_name, aliaz)
38
+ @relation_name = relation_name
39
+ @aliased = relation_name.dataset != aliaz
40
+ @key = aliased? ? aliaz : relation_name.dataset
41
+ end
42
+
43
+ def aliased?
44
+ @aliased
45
+ end
46
+
47
+ def inspect
48
+ if aliased?
49
+ "#{self.class}(#{relation_name.to_s} as #{key})"
50
+ else
51
+ "#{self.class}(#{relation_name.to_s})"
52
+ end
53
+ end
54
+ alias_method :to_s, :inspect
55
+
56
+ def dataset
57
+ relation_name.dataset
58
+ end
59
+
60
+ def relation
61
+ relation_name.relation
62
+ end
63
+
64
+ def sql_literal_append(ds, sql)
65
+ ds.literal_append(sql, dataset)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end