Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing to an SQLite database #291

Closed
Nintorac opened this issue Nov 10, 2023 · 9 comments · Fixed by #299
Closed

Writing to an SQLite database #291

Nintorac opened this issue Nov 10, 2023 · 9 comments · Fixed by #299

Comments

@Nintorac
Copy link
Contributor

Nintorac commented Nov 10, 2023

Hey, great project love the versatility it offers!

I am having an issue trying to write to a SQLite database, I have the following configuration, and I get this error SQLite databases do not support creating new schemas.

Am I doing something stupid or is this not possible to do?

Thanks!

dbt_project.yml

models:
  raddd_dbt:
    +materialized: table
    render_midis:
      +materialized: incremental
      +database: audio

profiles.yml

raddd_dbt:
  outputs:
    dev:
      type: duckdb
      path: ../data/dev.duckdb
      threads: 1
      attach:
        - path: ../data/audio.db
          type: sqlite
  target: dev
@jwills
Copy link
Collaborator

jwills commented Nov 10, 2023

Yeah my guess here is that dbt-duckdb is trying to create a schema in the SQLite database that it is writing to, since that is usually what it does when it’s writing a DuckDB model. There is almost certainly a way to turn that off and have dbt-duckdb just write the database + table name, but I’m on the road and I’m not sure I could figure it out from a phone— sorry!

@Nintorac
Copy link
Contributor Author

Hmm, ok thanks! I will keep digging :)

Enjoy your road trip!! (if that's why you're on the road)

@jwills
Copy link
Collaborator

jwills commented Nov 11, 2023

Yeah looked at this a bit more now that I'm at a computer and there doesn't seem to be a great way to do it-- i.e., sqlite never wants a schema, and dbt always wants a schema on a table. Maybe try creating a view model in DuckDB and then adding a post-hook that does the CREATE TABLE <sqlitedb>.<table_name> AS SELECT * FROM {{ this }} trick?

@jwills
Copy link
Collaborator

jwills commented Nov 11, 2023

the only other way I could think to do it is with a custom materialization, but that seems like a lot of work

@Nintorac
Copy link
Contributor Author

Nintorac commented Nov 12, 2023

Thanks for looking into it!

adding a post-hook

for my situation this won't work, the model is Python based so only allows incremental or table and the model is too large to create in a single call so I need incremental

custom materialization

yea, that does seem like a lot of work, especially since you would probably need to implement one for every "real" DBT materialisation method.


I'm actually a bit confused about why it would be trying to create the schema in the first place, I would have thought by deafult that it would be trying to write to the main schema which should already exist so creation should not be necessary.

I tried forcing it to use main by modifying the config like this

    render_midis:
      +materialized: incremental
      +database: audio
      +schema: main

But that results in the same error.

I then made a downstream dummy SQL table that looks like this

SELECT * from {{ref("render_midis")}} 

Then compiled it, and got this as a result

SELECT * from "audio"."main_main"."render_midis"

Then just to see what would happen I also tried modifying the schema to something else, audio in my test, so the config was like this

    render_midis:
      +materialized: incremental
      +database: audio
      +schema: audio

That compiles to

SELECT * from "audio"."main_audio"."render_midis"

Not sure what to make of that myself, any ideas?

@Nintorac
Copy link
Contributor Author

Ooh, now I tried with this config (deleted the schema setting)

    render_midis:
      +materialized: incremental
      +database: audio

and the result is

SELECT * from "audio"."main"."render_midis"

Then running this query against DuckDB will successfully create the table

ATTACH 'audio.db' (TYPE sqlite);
CREATE TABLE "audio"."main"."hello" AS SELECT 1

So maybe there just needs to be a conditional here that checks if the target is a SQLite and skips the call if so. Maybe also it should assert that the schema option is unset?

And the compilation SELECT * from "audio"."main_main"."render_midis" is expected DBT behaviour ..lol I think I knew that at one point 😅

@jwills
Copy link
Collaborator

jwills commented Nov 12, 2023

Ah, the dbt custom schema mistake strikes again! I should have known! 😉

@jwills
Copy link
Collaborator

jwills commented Nov 12, 2023

(Like any useful piece of technology, dbt got 95% of things right that you never notice/think about, and 5% of things wrong that cause pain and suffering for everyone forever)

@Nintorac
Copy link
Contributor Author

So this seems to do the job :)

My first jinja script so open to any comments/criticisms. Happy to open a PR too

{% macro duckdb__create_schema(relation) -%}
  {%- call statement('create_schema') -%}
    {% set sql %}
        select type from duckdb_databases()
        where database_name='{{ relation.database }}'
        and type='sqlite'
    {% endset %}
    {% set results = run_query(sql) %}
    {% if results|length == 0 %}
        create schema if not exists {{ relation.without_identifier() }}
    {% else %}
        {% if relation.schema!='main' %}
            {{ exceptions.raise_compiler_error(
                "Schema must be 'main' when writing to sqlite "
                ~ "instead got " ~ relation.schema
            )}}
        {% endif %}
    {% endif %}
  {%- endcall -%}
{% endmacro %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants