activerecord-cockroachdb-adapter 0.3.0.beta1 → 5.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ca5207eae1d4bf1c26da960ad80e78f54099b1eb62466e89ff5186c9d41632
4
- data.tar.gz: d3d8a5c38a3adda552b283663d4aaf88e05587b3ac9e0da28418fa05d72cfcdd
3
+ metadata.gz: a28ccd96cddcfaf334a3a854f8312a754e11b3a610ce338e53465abc567dcd71
4
+ data.tar.gz: 37ea9e0cb5d202128a46fbeb97fce75a9ef6aa8ae8043fc2c1f11fc38ae01ff1
5
5
  SHA512:
6
- metadata.gz: 955dca7f67f68b93fd2ebd19cd6027de446598c823abbc108ce5630fbf6d566c14c09e283f37e3d04fb306ff72a68e55f6856daea91a61f9488636f9da957920
7
- data.tar.gz: 9cb056da8d37cd199427f2ae25d354bcb3fb606fde92e1c01fb2cef7eedd2122a9eccbf0b3693d7d0c7197f8ff8d7d4b1321d16d9212db4331673ddf5669b934
6
+ metadata.gz: 2a3a26fe9bbd9043bbbcd50b1c262cdc88bfded513b8f4d1fb2488d0a76cc9943c8201c082f6a6a5c65123801ad3fa10851ff708e771f3be9b07b53464ed4ae5
7
+ data.tar.gz: f5e123cce6d782b4427b4e97066cd874b9539a4a7de78d122ed46672326864bf4640aa3a9810b506f3bd307e6a1ff2fdf9447f2e215a391022350e023c41278d
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
data/CONTRIBUTING.md CHANGED
@@ -46,19 +46,31 @@ Using [bundler](http://bundler.io/), install the dependancies of Rails.
46
46
  bundle install
47
47
  ```
48
48
 
49
- Then, to run the full ActiveRecord test suite with an active CockroachDB instance:
49
+ Then, to run the full test suite with an active CockroachDB instance:
50
50
 
51
51
  ```bash
52
52
  bundle exec rake test
53
53
  ```
54
54
 
55
- To run specific tests, set environemnt variable `TEST_FILES_AR`. For example, to run ActiveRecord tests `test/cases/associations_test.rb` and `test/cases/ar_schema_test.rb.rb`
55
+ To run specific ActiveRecord tests, set environemnt variable `TEST_FILES_AR`. For example, to run ActiveRecord tests `test/cases/associations_test.rb` and `test/cases/ar_schema_test.rb.rb`
56
56
 
57
57
  ```bash
58
58
  TEST_FILES_AR="test/cases/associations_test.rb,test/cases/ar_schema_test.rb" bundle exec rake test
59
59
  ```
60
60
 
61
- By default, tests will be run from the bundled version of Rails. To run against a local copy, set environemnt variable `RAILS_SOURCE`.
61
+ To run specific CockroachDB Adapter tests, set environemnt variable `TEST_FILES`. For example, to run CockroachDB Adpater tests `test/cases/adapter_test.rb` and `test/cases/associations/left_outer_join_association_test.rb`
62
+
63
+ ```bash
64
+ TEST_FILES="test/cases/adapter_test.rb,test/cases/associations/left_outer_join_association_test.rb" bundle exec rake test
65
+ ```
66
+
67
+ To run a specific test case, use minitest's `-n` option to run tests that match a given pattern. All minitest options are set via the `TESTOPTS` environemnt variable. For example, to run `test_indexes` from CockroachDB's `test/cases/adapter_test.rb` file
68
+
69
+ ```bash
70
+ TEST_FILES="test/cases/adapter_test.rb" TESTOPTS=`-n=/test_indexes/` bundle exec rake test
71
+ ```
72
+
73
+ By default, tests will be run from the bundled version of Rails. To run against a local copy, set environemnt variable `RAILS_SOURCE`. Running against a local copy of Rails can be helpful when try to debug issues.
62
74
 
63
75
  ```bash
64
76
  RAILS_SOURCE="path/to/local_copy" bundle exec rake test
data/README.md CHANGED
@@ -7,7 +7,7 @@ CockroachDB adapter for ActiveRecord 4 and 5. This is a lightweight extension of
7
7
  Add this line to your project's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'activerecord-cockroachdb-adapter', '~> 0.2'
10
+ gem 'activerecord-cockroachdb-adapter', '~> 5.2'
11
11
  ```
12
12
 
13
13
  If you're using Rails 4.x, use the `0.1.x` versions of this gem.
@@ -21,7 +21,303 @@ development:
21
21
  host: <hostname>
22
22
  user: <username>
23
23
  ```
24
+ ## Configuration
24
25
 
26
+ In addition to the standard adapter settings, CockroachDB also supports the following:
27
+
28
+ - `disable_cockroachdb_telemetry`: Determines if a telemetry call is made to the database when the connection pool is initialized. Setting this to `true` will prevent the call from being made.
29
+
30
+ ## Working with Spatial Data
31
+
32
+ The adapter uses [RGeo](https://github.com/rgeo/rgeo) and [RGeo-ActiveRecord](https://github.com/rgeo/rgeo-activerecord) to represent geometric and geographic data as Ruby objects and easily interface them with the adapter. The following is a brief introduction to RGeo and tips to help setup your spatial application. More documentation about RGeo can be found in the [YARD Docs](https://rubydoc.info/github/rgeo/rgeo) and [wiki](https://github.com/rgeo/rgeo/wiki).
33
+
34
+ ### Installing RGeo
35
+
36
+ RGeo can be installed with the following command:
37
+
38
+ ```sh
39
+ gem install rgeo
40
+ ```
41
+
42
+ The best way to use RGeo is with GEOS support. If you have a version of libgeos installed, you can check that it was properly linked with RGeo by running the following commands:
43
+
44
+ ```rb
45
+ require 'rgeo'
46
+
47
+ RGeo::Geos.supported?
48
+ #=> true
49
+ ```
50
+
51
+ If this is `false`, you may need to specify the GEOS directory while installing. Here's an example linking it to the CockroachDB GEOS binary.
52
+
53
+ ```sh
54
+ gem install rgeo -- --with-geos-dir=/path/to/cockroach/lib/
55
+ ```
56
+
57
+ ### Working with RGeo
58
+
59
+ RGeo uses [factories](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)) to create geometry objects and define their properties. Different factories define their own implementations for standard methods. For instance, the `RGeo::Geographic.spherical_factory` accepts latitudes and longitues as its coordinates and does computations on a spherical surface, while `RGeo::Cartesian.factory` implements geometry objects on a plane.
60
+
61
+ The factory (or factories) you choose to use will depend on the requirements of your application and what you need to do with the geometries they produce. For example, if you are working with points or other simple geometries across long distances and need precise results, the spherical factory is a good choice. If you're working with polygons or multipolygons and analyzing complex relationships between them (`intersects?`, `difference`, etc.), then using a cartesian factory backed by GEOS is a much better option.
62
+
63
+ Once you've selected a factory, you need to create objects. RGeo supports geometry creation through standard constructors (`point`, `line_string`, `polygon`, etc.) or by WKT and WKB.
64
+
65
+ ```rb
66
+ require 'rgeo'
67
+ factory = RGeo::Cartesian.factory(srid: 3857)
68
+
69
+ # Create a line_string from points
70
+ pt1 = factory.point(0,0)
71
+ pt2 = factory.point(1,1)
72
+ pt3 = factory.point(2,2)
73
+ line_string = factory.line_string([pt1,pt2,pt3])
74
+
75
+ p line_string.length
76
+ #=> 2.8284271247461903
77
+
78
+ # check line_string equality
79
+ line_string2 = factory.parse_wkt("LINESTRING (0 0, 1 1, 2 2)")
80
+ p line_string == line_string2
81
+ #=> true
82
+
83
+ # create polygon and test intersection with line_string
84
+ pt4 = factory.point(0,2)
85
+ outer_ring = factory.linear_ring([pt1,pt2,pt3,pt4,pt1])
86
+ poly = factory.polygon(outer_ring)
87
+
88
+ p line_string.intersects? poly
89
+ #=> true
90
+ ```
91
+ ### Creating Spatial Tables
92
+
93
+ To store spatial data, you must create a column with a spatial type. PostGIS
94
+ provides a variety of spatial types, including point, linestring, polygon, and
95
+ different kinds of collections. These types are defined in a standard produced
96
+ by the Open Geospatial Consortium. You can specify options indicating the coordinate system and number of coordinates for the values you are storing.
97
+
98
+ The adapter extends ActiveRecord's migration syntax to
99
+ support these spatial types. The following example creates five spatial
100
+ columns in a table:
101
+
102
+ ```rb
103
+ create_table :my_spatial_table do |t|
104
+ t.column :shape1, :geometry
105
+ t.geometry :shape2
106
+ t.line_string :path, srid: 3857
107
+ t.st_point :lonlat, geographic: true
108
+ t.st_point :lonlatheight, geographic: true, has_z: true
109
+ end
110
+ ```
111
+
112
+ The first column, "shape1", is created with type "geometry". This is a general
113
+ "base class" for spatial types; the column declares that it can contain values
114
+ of _any_ spatial type.
115
+
116
+ The second column, "shape2", uses a shorthand syntax for the same type as the shape1 column.
117
+ You can create a column either by invoking `column` or invoking the name of the type directly.
118
+
119
+ The third column, "path", has a specific geometric type, `line_string`. It
120
+ also specifies an SRID (spatial reference ID) that indicates which coordinate
121
+ system it expects the data to be in. The column now has a "constraint" on it;
122
+ it will accept only LineString data, and only data whose SRID is 3857.
123
+
124
+ The fourth column, "lonlat", has the `st_point` type, and accepts only Point
125
+ data. Furthermore, it declares the column as "geographic", which means it
126
+ accepts longitude/latitude data, and performs calculations such as distances
127
+ using a spheroidal domain.
128
+
129
+ The fifth column, "lonlatheight", is a geographic (longitude/latitude) point
130
+ that also includes a third "z" coordinate that can be used to store height
131
+ information.
132
+
133
+ The following are the data types understood by PostGIS and exposed by
134
+ the adapter:
135
+
136
+ - `:geometry` -- Any geometric type
137
+ - `:st_point` -- Point data
138
+ - `:line_string` -- LineString data
139
+ - `:st_polygon` -- Polygon data
140
+ - `:geometry_collection` -- Any collection type
141
+ - `:multi_point` -- A collection of Points
142
+ - `:multi_line_string` -- A collection of LineStrings
143
+ - `:multi_polygon` -- A collection of Polygons
144
+
145
+ Following are the options understood by the adapter:
146
+
147
+ - `:geographic` -- If set to true, create a PostGIS geography column for
148
+ longitude/latitude data over a spheroidal domain; otherwise create a
149
+ geometry column in a flat coordinate system. Default is false. Also
150
+ implies :srid set to 4326.
151
+ - `:srid` -- Set a SRID constraint for the column. Default is 4326 for a
152
+ geography column, or 0 for a geometry column. Note that PostGIS currently
153
+ (as of version 2.0) requires geography columns to have SRID 4326, so this
154
+ constraint is of limited use for geography columns.
155
+ - `:has_z` -- Specify that objects in this column include a Z coordinate.
156
+ Default is false.
157
+ - `:has_m` -- Specify that objects in this column include an M coordinate.
158
+ Default is false.
159
+
160
+ To create a PostGIS spatial index, add `using: :gist` to your index:
161
+
162
+ ```rb
163
+ add_index :my_table, :lonlat, using: :gist
164
+
165
+ # or
166
+
167
+ change_table :my_table do |t|
168
+ t.index :lonlat, using: :gist
169
+ end
170
+ ```
171
+ ### Configuring ActiveRecord
172
+
173
+ ActiveRecord's usefulness stems from the way it automatically configures
174
+ classes based on the database structure and schema. If a column in the
175
+ database has an integer type, ActiveRecord automatically casts the data to a
176
+ Ruby Integer. In the same way, the adapter automatically
177
+ casts spatial data to a corresponding RGeo data type.
178
+
179
+ RGeo offers more flexibility in its type system than can be
180
+ interpreted solely from analyzing the database column. For example, you can
181
+ configure RGeo objects to exhibit certain behaviors related to their
182
+ serialization, validation, coordinate system, or computation. These settings
183
+ are embodied in the RGeo factory associated with the object.
184
+
185
+ You can configure the adapter to use a particular factory (i.e. a
186
+ particular combination of settings) for data associated with each type in
187
+ the database.
188
+
189
+ Here's an example using a Geos default factory:
190
+
191
+ ```ruby
192
+ RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
193
+ # By default, use the GEOS implementation for spatial columns.
194
+ config.default = RGeo::Geos.factory_generator
195
+
196
+ # But use a geographic implementation for point columns.
197
+ config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "point")
198
+ end
199
+ ```
200
+
201
+ The default spatial factory for geographic columns is `RGeo::Geographic.spherical_factory`.
202
+ The default spatial factory for cartesian columns is `RGeo::Cartesian.preferred_factory`.
203
+ You do not need to configure the `SpatialFactoryStore` if these defaults are ok.
204
+
205
+ More information about configuration options for the `SpatialFactoryStore` can be found in the [rgeo-activerecord](https://github.com/rgeo/rgeo-activerecord#spatial-factories-for-columns) docs.
206
+
207
+ ### Reading and Writing Spatial Columns
208
+
209
+ When you access a spatial attribute on your ActiveRecord model, it is given to
210
+ you as an RGeo geometry object (or nil, for attributes that allow null
211
+ values). You can then call the RGeo api on the object. For example, consider
212
+ the MySpatialTable class we worked with above:
213
+
214
+ ```rb
215
+ record = MySpatialTable.find(1)
216
+ point = record.lonlat # Returns an RGeo::Feature::Point
217
+ p point.x # displays the x coordinate
218
+ p point.geometry_type.type_name # displays "Point"
219
+ ```
220
+
221
+ The RGeo factory for the value is determined by how you configured the
222
+ ActiveRecord class, as described above. In this case, we explicitly set a
223
+ spherical factory for the `:lonlat` column:
224
+
225
+ ```rb
226
+ factory = point.factory # returns a spherical factory
227
+ ```
228
+
229
+ You can set a spatial attribute by providing an RGeo geometry object, or by
230
+ providing the WKT string representation of the geometry. If a string is
231
+ provided, the adapter will attempt to parse it as WKT and
232
+ set the value accordingly.
233
+
234
+ ```rb
235
+ record.lonlat = 'POINT(-122 47)' # sets the value to the given point
236
+ ```
237
+
238
+ If the WKT parsing fails, the value currently will be silently set to nil. In
239
+ the future, however, this will raise an exception.
240
+
241
+ ```rb
242
+ record.lonlat = 'POINT(x)' # sets the value to nil
243
+ ```
244
+
245
+ If you set the value to an RGeo object, the factory needs to match the factory
246
+ for the attribute. If the factories do not match, the adapter
247
+ will attempt to cast the value to the correct factory.
248
+
249
+ ```rb
250
+ p2 = factory.point(-122, 47) # p2 is a point in a spherical factory
251
+ record.lonlat = p2 # sets the value to the given point
252
+ record.shape1 = p2 # shape1 uses a flat geos factory, so it
253
+ # will cast p2 into that coordinate system
254
+ # before setting the value
255
+ record.save
256
+ ```
257
+
258
+ If you attempt to set the value to the wrong type, such as setting a linestring attribute to a point value, you will get an exception from the database when you attempt to save the record.
259
+
260
+ ```rb
261
+ record.path = p2 # This will appear to work, but...
262
+ record.save # This will raise an exception from the database
263
+ ```
264
+
265
+ ### Spatial Queries
266
+
267
+ You can create simple queries based on representational equality in the same
268
+ way you would on a scalar column:
269
+
270
+ ```ruby
271
+ record2 = MySpatialTable.where(:lonlat => factory.point(-122, 47)).first
272
+ ```
273
+
274
+ You can also use WKT:
275
+
276
+ ```ruby
277
+ record3 = MySpatialTable.where(:lonlat => 'POINT(-122 47)').first
278
+ ```
279
+
280
+ Note that these queries use representational equality, meaning they return
281
+ records where the lonlat value matches the given value exactly. A 0.00001
282
+ degree difference would not match, nor would a different representation of the
283
+ same geometry (like a multi_point with a single element). Equality queries
284
+ aren't generally all that useful in real world applications. Typically, if you
285
+ want to perform a spatial query, you'll look for, say, all the points within a
286
+ given area. For those queries, you'll need to use the standard spatial SQL
287
+ functions provided by PostGIS.
288
+
289
+ To perform more advanced spatial queries, you can use the extended Arel interface included in the adapter. The functions accept WKT strings or RGeo features.
290
+
291
+ ```rb
292
+ point = RGeo::Geos.factory(srid: 0).point(1,1)
293
+
294
+ # Example Building model where geom is a column of polygons.
295
+ buildings = Building.arel_table
296
+ containing_buiildings = Building.where(buildings[:geom].st_contains(point))
297
+ ```
298
+
299
+ See the [rgeo-activerecord YARD Docs](https://rubydoc.info/github/rgeo/rgeo-activerecord/RGeo/ActiveRecord/SpatialExpressions) for a list of available PostGIS functions.
300
+
301
+ ### Validation Issues
302
+
303
+ If you see an `RGeo::Error::InvalidGeometry (LinearRing failed ring test)` message while loading data or creating geometries, this means that the geometry you are trying to instantiate is not topologically valid. This is usually due to self-intersections in the geometry. The default behavior of RGeo factories is to raise this error when an invalid geometry is being instansiated, but this can be ignored by setting the `uses_lenient_assertions` flag to `true` when creating your factory.
304
+
305
+ ```rb
306
+ regular_fac = RGeo::Geographic.spherical_factory
307
+ modified_fac = RGeo::Geographic.spherical_factory(uses_lenient_assertions: true)
308
+
309
+ wkt = "POLYGON (0 0, 1 1, 0 1, 1 0, 0 0)" # closed ring with self intersection
310
+
311
+ regular_fac.parse_wkt(wkt)
312
+ #=> RGeo::Error::InvalidGeometry (LinearRing failed ring test)
313
+
314
+ p modified_fac.parse_wkt(wkt)
315
+ #=> #<RGeo::Geographic::SphericalPolygonImpl>
316
+ ```
317
+
318
+ Be careful when performing calculations on potentially invalid geometries, as the results might be nonsensical. For example, the area returned of an hourglass made of 2 equivalent triangles with a self-intersection in the middle is 0.
319
+
320
+ Note that when using the `spherical_factory`, there is a chance that valid geometries will be interpreted as invalid due to floating point issues with small geometries.
25
321
 
26
322
  ## Modifying the adapter?
27
323
 
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "activerecord-cockroachdb-adapter"
7
- spec.version = "0.3.0.beta1"
7
+ spec.version = "5.2.3"
8
8
  spec.licenses = ["Apache-2.0"]
9
9
  spec.authors = ["Cockroach Labs"]
10
10
  spec.email = ["cockroach-db@googlegroups.com"]
@@ -15,6 +15,7 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.add_dependency "activerecord", "~> 5.2"
17
17
  spec.add_dependency "pg", ">= 0.20"
18
+ spec.add_dependency "rgeo-activerecord", "~> 7.0.0"
18
19
 
19
20
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
21
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -11,6 +11,7 @@ connections:
11
11
  user: root
12
12
  requiressl: disable
13
13
  min_messages: warning
14
+ disable_cockroachdb_telemetry: true
14
15
  arunit_without_prepared_statements:
15
16
  database: activerecord_unittest
16
17
  host: localhost
@@ -19,6 +20,7 @@ connections:
19
20
  requiressl: disable
20
21
  min_messages: warning
21
22
  prepared_statements: false
23
+ disable_cockroachdb_telemetry: true
22
24
  arunit2:
23
25
  database: activerecord_unittest2
24
26
  host: localhost
@@ -26,3 +28,4 @@ connections:
26
28
  user: root
27
29
  requiressl: disable
28
30
  min_messages: warning
31
+ disable_cockroachdb_telemetry: true
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- set -euo pipefail
3
+ set -euox pipefail
4
4
 
5
5
  # Download CockroachDB
6
- VERSION=v20.1.0-rc.1
6
+ VERSION=v20.2.3
7
7
  wget -qO- https://binaries.cockroachdb.com/cockroach-$VERSION.linux-amd64.tgz | tar xvz
8
8
  readonly COCKROACH=./cockroach-$VERSION.linux-amd64/cockroach
9
9
 
@@ -21,7 +21,7 @@ run_cockroach() {
21
21
  cockroach quit --insecure || true
22
22
  rm -rf cockroach-data
23
23
  # Start CockroachDB.
24
- cockroach start --insecure --host=localhost --listening-url-file="$urlfile" >/dev/null 2>&1 &
24
+ cockroach start-single-node --max-sql-memory=25% --cache=25% --insecure --host=localhost --spatial-libs=./cockroach-$VERSION.linux-amd64/lib --listening-url-file="$urlfile" >/dev/null 2>&1 &
25
25
  # Ensure CockroachDB is stopped on script exit.
26
26
  trap "echo 'Exit routine: Killing CockroachDB.' && kill -9 $! &> /dev/null" EXIT
27
27
  # Wait until CockroachDB has started.
@@ -33,6 +33,9 @@ run_cockroach() {
33
33
  done
34
34
  cockroach sql --insecure -e 'CREATE DATABASE activerecord_unittest;'
35
35
  cockroach sql --insecure -e 'CREATE DATABASE activerecord_unittest2;'
36
+ cockroach sql --insecure -e 'SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false;'
37
+ cockroach sql --insecure -e 'SET CLUSTER SETTING sql.stats.histogram_collection.enabled = false;'
38
+ cockroach sql --insecure -e "SET CLUSTER SETTING jobs.retention_time = '180s';"
36
39
  }
37
40
 
38
41
  # Install ruby dependencies.
@@ -41,7 +44,7 @@ bundle install
41
44
 
42
45
  run_cockroach
43
46
 
44
- if ! (bundle exec rake test); then
47
+ if ! (RUBYOPT="-W0" TESTOPTS="-v" bundle exec rake test); then
45
48
  echo "Tests failed"
46
49
  HAS_FAILED=1
47
50
  else
data/docker.sh CHANGED
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # This file is largely cargo-culted from cockroachdb/cockroach/build/builder.sh.
4
4
 
5
- set -euo pipefail
5
+ set -euox pipefail
6
6
 
7
7
  DOCKER_IMAGE_TAG=activerecord_test_container
8
8