Просмотр исходного кода

docs: update dev-quick-ref guide for snowflake bridge

Ivan Dyachkov 1 год назад
Родитель
Сommit
e4a5cccbae
1 измененных файлов с 136 добавлено и 0 удалено
  1. 136 0
      apps/emqx_bridge_snowflake/docs/dev-quick-ref.md

+ 136 - 0
apps/emqx_bridge_snowflake/docs/dev-quick-ref.md

@@ -1,5 +1,41 @@
+## Initialize Snowflake ODBC driver
+
+### Linux
+
+Run `scripts/install-snowflake-driver.sh` to install the Snowflake ODBC driver and configure `odbc.ini`.
+
+### macOS
+
+- Install unixODBC (e.g. `brew install unixodbc`)
+- [Download and install iODBC](https://github.com/openlink/iODBC/releases/download/v3.52.16/iODBC-SDK-3.52.16-macOS11.dmg)
+- [Download and install the Snowflake ODBC driver](https://sfc-repo.snowflakecomputing.com/odbc/macuniversal/3.3.2/snowflake_odbc_mac_64universal-3.3.2.dmg)
+- Refer to [Installing and configuring the ODBC Driver for macOS](https://docs.snowflake.com/en/developer-guide/odbc/odbc-mac) for more information.
+- Update `~/.odbc.ini` and `/opt/snowflake/snowflakeodbc/lib/universal/simba.snowflake.ini`:
+
+```sh
+chown $(id -u):$(id -g) /opt/snowflake/snowflakeodbc/lib/universal/simba.snowflake.ini
+echo 'ODBCInstLib=libiodbcinst.dylib' >> /opt/snowflake/snowflakeodbc/lib/universal/simba.snowflake.ini
+
+cat < EOF > ~/.odbc.ini
+[ODBC]
+Trace=no
+TraceFile=
+
+[ODBC Drivers]
+Snowflake = Installed
+
+[ODBC Data Sources]
+snowflake = Snowflake
+
+[Snowflake]
+Driver = /opt/snowflake/snowflakeodbc/lib/universal/libSnowflake.dylib
+EOF
+```
+
 ## Basic helper functions
 ## Basic helper functions
 
 
+### Elixir
+
 ```elixir
 ```elixir
 Application.ensure_all_started(:odbc)
 Application.ensure_all_started(:odbc)
 user = "your_admin_user"
 user = "your_admin_user"
@@ -17,13 +53,31 @@ dsn = "snowflake"
 query = fn conn, sql -> :odbc.sql_query(conn, sql |> to_charlist()) end
 query = fn conn, sql -> :odbc.sql_query(conn, sql |> to_charlist()) end
 ```
 ```
 
 
+### Erlang
+
+```erlang
+application:ensure_all_started(odbc).
+User = "your_admin_user".
+Pass = os:getenv("SNOWFLAKE_PASSWORD").
+OrgID = "orgid".
+Account = "accountid".
+Server = lists:flatten([OrgID, "-", Account, ".snowflakecomputing.com"]).
+DSN = "snowflake".
+{ok, Conn} = odbc:connect(["dsn=snowflake;uid=", User, ";pwd=", Pass, ";server=", Server, ";account=", Account], []).
+Query = fun(Conn, Sql) -> odbc:sql_query(Conn, Sql) end.
+```
+
 ## Create user
 ## Create user
 
 
+### Shell
+
 ```sh
 ```sh
 openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_rsa_key.private.pem -nocrypt
 openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out snowflake_rsa_key.private.pem -nocrypt
 openssl rsa -in snowflake_rsa_key.private.pem -pubout -out snowflake_rsa_key.public.pem
 openssl rsa -in snowflake_rsa_key.private.pem -pubout -out snowflake_rsa_key.public.pem
 ```
 ```
 
 
+### Elixir
+
 ```elixir
 ```elixir
 test_user = "testuser"
 test_user = "testuser"
 query.(conn, "create user #{test_user} password = 'TestUser99' must_change_password = false")
 query.(conn, "create user #{test_user} password = 'TestUser99' must_change_password = false")
@@ -35,8 +89,26 @@ query.(conn, "alter user #{test_user} set rsa_public_key = '#{public_pem_content
 # {:updated, :undefined}
 # {:updated, :undefined}
 ```
 ```
 
 
+### Erlang
+
+```erlang
+TestUser = "testuser".
+Query(Conn, ["create user ", TestUser, " password = 'TestUser99' must_change_password = false"]).
+# {updated,undefined}
+
+{ok, Bin} = file:read_file("snowflake_rsa_key.public.pem").
+Pem = binary_to_list(Bin).
+[_ | Lines] = string:split(string:trim(Pem), "\n", all).
+PublicPemContentsTrimmed = lists:join("\n", lists:droplast(Lines)).
+
+Query(Conn, ["alter user ", TestUser, " set rsa_public_key = '", PublicPemContentsTrimmed, "'"]).
+# {updated,undefined}
+```
+
 ## Create database objects
 ## Create database objects
 
 
+### Elixir
+
 ```elixir
 ```elixir
 database = "testdatabase"
 database = "testdatabase"
 schema = "public"
 schema = "public"
@@ -92,3 +164,67 @@ query.(conn, "grant role #{test_role} to user #{test_user}")
 query.(conn, "alter user #{snowpipe_user} set default_role = #{snowpipe_role}")
 query.(conn, "alter user #{snowpipe_user} set default_role = #{snowpipe_role}")
 query.(conn, "alter user testuser set default_role = #{test_role}")
 query.(conn, "alter user testuser set default_role = #{test_role}")
 ```
 ```
+
+### Erlang
+
+```erlang
+Database = "testdatabase",
+Schema = "public",
+Table = "test1",
+Stage = "teststage0",
+Pipe = "testpipe0",
+Warehouse = "testwarehouse",
+SnowpipeRole = "snowpipe1",
+SnowpipeUser = "snowpipeuser",
+TestRole = "testrole",
+FqnTable = [Database, ".", Schema, ".", Table],
+FqnStage = [Database, ".", Schema, ".", Stage],
+FqnPipe = [Database, ".", Schema, ".", Pipe],
+
+Query(Conn, "use role accountadmin"),
+
+% Create database, table, stage, pipe, warehouse
+Query(Conn, ["create database if not exists ", Database]),
+Query(Conn, ["create or replace table ", FqnTable, " (clientid string, topic string, payload binary, publish_received_at timestamp_ltz)"]),
+Query(Conn, ["create stage if not exists ", FqnStage, " file_format = (type = csv parse_header = true) copy_options = (on_error = continue purge = true)"]),
+Query(Conn, ["create pipe if not exists ", FqnPipe, " as copy into ", FqnTable, " from @", FqnStage, " match_by_column_name = case_insensitive"]),
+Query(Conn, ["create or replace warehouse ", Warehouse]),
+
+% Create a role for the Snowpipe privileges.
+Query(Conn, ["create or replace role ", SnowpipeRole]),
+Query(Conn, ["create or replace role ", TestRole]),
+
+% Grant the USAGE privilege on the database and schema that contain the pipe object.
+Query(Conn, ["grant usage on database ", Database, " to role ", SnowpipeRole]),
+Query(Conn, ["grant usage on database ", Database, " to role ", TestRole]),
+Query(Conn, ["grant usage on schema ", Database, ".", Schema, " to role ", SnowpipeRole]),
+Query(Conn, ["grant usage on schema ", Database, ".", Schema, " to role ", TestRole]),
+
+% Grant the INSERT and SELECT privileges on the target table.
+Query(Conn, ["grant insert, select on ", FqnTable, " to role ", SnowpipeRole]),
+% For cleaning up table after tests
+Query(Conn, ["grant insert, select, truncate, delete on ", FqnTable, " to role ", TestRole]),
+
+% Grant the USAGE privilege on the external stage.
+% Must use read/write for internal stage
+% Query(Conn, ["grant usage on stage ", FqnStage, " to role ", SnowpipeRole]),
+Query(Conn, ["grant read, write on stage ", FqnStage, " to role ", SnowpipeRole]),
+% For cleaning up table after tests
+Query(Conn, ["grant read, write on stage ", FqnStage, " to role ", TestRole]),
+
+% Grant the OPERATE and MONITOR privileges on the pipe object.
+Query(Conn, ["grant operate, monitor on pipe ", FqnPipe, " to role ", SnowpipeRole]),
+
+% Grant the role to a user
+Query(Conn, ["create user if not exists ", SnowpipeUser, " password = 'TestUser99' must_change_password = false rsa_public_key = '", PublicPemContentsTrimmed, "'"]),
+
+Query(Conn, ["grant usage on warehouse ", Warehouse, " to role ", TestRole]),
+
+Query(Conn, ["grant role ", SnowpipeRole, " to user ", SnowpipeUser]),
+Query(Conn, ["grant role ", SnowpipeRole, " to user ", TestUser]),
+Query(Conn, ["grant role ", TestRole, " to user ", TestUser]),
+
+% Set the role as the default role for the user
+Query(Conn, ["alter user ", SnowpipeUser, " set default_role = ", SnowpipeRole]),
+Query(Conn, ["alter user testuser set default_role = ", TestRole]).
+```