pacificclimate / modelmeta

An ORM representation of the model metadata database
GNU General Public License v3.0
1 stars 0 forks source link

SQLite database creation errors #68

Open corviday opened 6 years ago

corviday commented 6 years ago

Following the sqlite test database creation instructions in this repository's README:

$ git clone https://github.com/pacificclimate/modelmeta
$ cd modelmeta/
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -i https://pypi.pacificclimate.org/simple -r requirements.txt
$ pip install .
$ emacs alembic.ini #add an entry for a test_creation SQLite database
$ alembic -x db=test_creation upgrade head

### Creating modelmeta ORM
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 7847aa3c1b39, initial create
Traceback (most recent call last):
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/default.py", line 507, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: near "extension": syntax error

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/bin/alembic", line 11, in <module>
    sys.exit(main())
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/config.py", line 479, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/config.py", line 473, in main
    self.run_cmd(cfg, options)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/config.py", line 456, in run_cmd
    **dict((k, getattr(options, k, None)) for k in kwarg)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/command.py", line 254, in upgrade
    script.run_env()
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/script/base.py", line 427, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/util/pyfiles.py", line 81, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/util/compat.py", line 83, in load_module_py
    spec.loader.exec_module(module)
  File "<frozen importlib._bootstrap_external>", line 665, in exec_module
  File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File "alembic/env.py", line 118, in <module>
    run_migrations_online()
  File "alembic/env.py", line 112, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/runtime/environment.py", line 836, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/runtime/migration.py", line 330, in run_migrations
    step.migration_fn(**kw)
  File "/home/lzeman/Code/modelmeta/modelmeta/alembic/versions/7847aa3c1b39_initial_create.py", line 24, in upgrade
    op.execute('create extension postgis')
  File "<string>", line 8, in execute
  File "<string>", line 3, in execute
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/operations/ops.py", line 1856, in execute
    return operations.invoke(op)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/operations/base.py", line 319, in invoke
    return fn(self, operation)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/operations/toimpl.py", line 161, in execute_sql
    execution_options=operation.execution_options
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/ddl/impl.py", line 121, in execute
    self._exec(sql, execution_options)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/alembic/ddl/impl.py", line 118, in _exec
    return conn.execute(construct, *multiparams, **params)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
    context)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
    exc_info
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 186, in reraise
    raise value.with_traceback(tb)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/home/lzeman/Code/modelmeta/modelmeta/venv/lib/python3.5/site-packages/sqlalchemy/engine/default.py", line 507, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "extension": syntax error [SQL: 'create extension postgis'] (Background on this error at: http://sqlalche.me/e/e3q8)

This procedure was previously working.

jameshiebert commented 6 years ago

Looks like a problem added by this commit. There's no PostGIS extension (or an extension system at all) in sqlite. @corviday can you experiment with whether this step in the migration could be constrained to PostgreSQL deployments (and @rod-glover could you comment on your intuition?)?

rod-glover commented 6 years ago

Yes, this step in the migration can certainly be constrained to PostgreSQL deployments. We already have some variant migration code for PosgtreSQL vs. SQLite, and the same mechanism can be applied to this.

Some notes:

  1. The original SQLite test databases modelmeta/data/mddb-v1.sqlite and modelmeta/data/mddb-v2.sqlite contain table spatial_ref_sys, which is the main effect (for us) of using PostGIS.

    1. In the v1 database, spatial_ref_sys contains data (quick eyeball inspection looks just like the data installed by PostGIS.
    2. In the v2 database, it contains no data.
    3. I suspect that in these databases (which were created before the introduction of Alembic), spatial_ref_sys was created and loaded from a dump of a Postgres database (i.e., pcic_meta) at some point.
  2. It would be easy to add variant code for SQLite in the initial migration to create (and populate, if desired) the table spatial_ref_sys.

rod-glover commented 6 years ago

In working on another issue, I implemented variant code for SQLite in the initial create migration and it worked. (And was easy to do.)

However, attempting to migrate to 12f290b63791, handle variant sampling geometries on a SQLite database revealed that foreign key constraints are not named automatically for SQLite (as they are for Postgres). The migration fails in SQLite when it tries to drop the constraint (with nonexistent name) data_file_variables_grid_id_fkey. The constraint exists, but it is not named.

To see this, compare the following CREATE TABLE commands extracted from the Postgres and SQLite databases respectively after migration to initial create:

Postgres:

CREATE TABLE data_file_variables
(
  data_file_variable_id serial NOT NULL,
  derivation_method character varying(255),
  variable_cell_methods character varying(255),
  netcdf_variable_name character varying(32) NOT NULL,
  disabled boolean,
  range_min double precision NOT NULL,
  range_max double precision NOT NULL,
  data_file_id integer NOT NULL,
  variable_alias_id integer NOT NULL,
  level_set_id integer,
  grid_id integer NOT NULL,
  CONSTRAINT data_file_variables_pkey PRIMARY KEY (data_file_variable_id),
  CONSTRAINT data_file_variables_data_file_id_fkey FOREIGN KEY (data_file_id)
      REFERENCES data_files (data_file_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT data_file_variables_grid_id_fkey FOREIGN KEY (grid_id)
      REFERENCES grids (grid_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_file_variables_level_set_id_fkey FOREIGN KEY (level_set_id)
      REFERENCES level_sets (level_set_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_file_variables_variable_alias_id_fkey FOREIGN KEY (variable_alias_id)
      REFERENCES variable_aliases (variable_alias_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

SQLite:

CREATE TABLE data_file_variables 
( 
    data_file_variable_id INTEGER NOT NULL, 
    derivation_method VARCHAR(255), 
    variable_cell_methods VARCHAR(255), 
    netcdf_variable_name VARCHAR(32) NOT NULL, 
    disabled BOOLEAN, 
    range_min FLOAT NOT NULL, 
    range_max FLOAT NOT NULL, 
    data_file_id INTEGER NOT NULL, 
    variable_alias_id INTEGER NOT NULL, 
    level_set_id INTEGER, 
    grid_id INTEGER NOT NULL, 
    PRIMARY KEY (data_file_variable_id), 
    CONSTRAINT data_file_variables_data_file_id_fkey FOREIGN KEY(data_file_id) REFERENCES data_files (data_file_id) ON DELETE CASCADE, 
    FOREIGN KEY(grid_id) REFERENCES grids (grid_id), 
    FOREIGN KEY(level_set_id) REFERENCES level_sets (level_set_id), 
    FOREIGN KEY(variable_alias_id) REFERENCES variable_aliases (variable_alias_id), 
    CHECK (disabled IN (0, 1)) 
)

The solution to this is to provide explicit names in the ORM for each foreign key constraint in the database, as in this existing example (which might have arisen for just this reason, or for another ...).

Since it is easy, it would probably be a good idea to do this for all foreign keys just so this doesn't bite us again in some future migration. We should use the existing automatically generated FK constraint names in a Postgres database. This is easy enough to do with an inspection tool like pgAdmin or by inspecting/grepping a dump of, e.g., the ce_meta database if one happens to be hanging around.