Utilizing macro to drop multiple schemas from a snowflake database based on conditions - macros

{% macro drop_schema_str(schema) %}
{% set relation = api.Relation.create(database=target.database, schema=schema) %}
{% do drop_schema(relation) %}
{% endmacro %}
This macro works perfectly. But I'd like to drop a bunch of schemas at a time. I couldn't figure out how to add this query into this macro and run it?
SELECT
SCHEMA_NAME
FROM
`{{ target.database }}.INFORMATION_SCHEMA.SCHEMATA`
WHERE
SCHEMA_NAME != 'INFORMATION_SCHEMA'
AND SCHEMA_NAME ILIKE '%DEV_EL_%'
I tried for-loop methods in the macro but while running it continued to give errors.Thank you

I believe the reason this isn't working is that you're trying to execute some SQL to populate your SQL query. In principle that isn't impossible, but DBT constructs models in two stages:
compile the SQL queries from your templates
execute those queries in the appropriate sequence
So I imagine that when your macro tries to compile, it's trying to create a set of SQL queries to drop relations, but the information needed for those queries is not yet available.
Fortunately, I think they have you covered. There is a special construction, {% if execute %}, which appears to be designed for exactly your use case!
Adapting the example from the docs, it may be possible to take the following and append a for loop with calls to your macro:
{% set to_drop_query %}
SELECT
SCHEMA_NAME
FROM
`{{ target.database }}.INFORMATION_SCHEMA.SCHEMATA`
WHERE
SCHEMA_NAME != 'INFORMATION_SCHEMA'
AND SCHEMA_NAME ILIKE '%DEV_EL_%'
{% endset %}
{% set results = run_query(to_drop_query) %}
{% if execute %}
{# Return the first column #}
{% set relations_to_drop = results.columns[0].values() %}
{% endif %}

Related

Nifi expression language for postgres "insert into" single quote varchars

I am trying to use nifi expression for
INSERT INTO public.demo (col1,col2,col3) VALUES (?,?,?) ON CONFLICT (col1) DO UPDATE set col2 = '${sql.args.2.value:replaceAll("*regex for single quote*","regex for double quote ")}'
So col2 is in a Oracle database often having a single quote.
Ex. " user's " which is creating problems for postgres insertion as postres uses a single quote to start and end varchar values.
I read about nifi expression language and am using it like the above but its throwing a syntax error.
What should it be like?
I guess I also need to be replacing the ? In values With the same sql.args.

How to execute sql by getting dynamic table from mybatis without using ${}?

I am trying to execute an sql statement by receiving a dynamic table.
If #{} is used, sql does not work because it is entered in the form of'layer_name'.
Therefore, I have to use ${}, but there is a problem that is vulnerable to sql injection.
<select id="test" parameterType="map" resultType="map">
select
*
from
${table_name}
</select>
I want to use a procedure to solve this problem, but the return type is ambiguous because I have to do 'select all'.
Any ideas would be appreciated.

Dynamic SELECT INTO in PL/pgSQL function

How can I write a dynamic SELECT INTO query inside a PL/pgSQL function in Postgres?
Say I have a variable called tb_name which is filled in a FOR loop from information_schema.tables. Now I have a variable called tc which will be taking the row count for each table. I want something like the following:
FOR tb_name in select table_name from information_schema.tables where table_schema='some_schema' and table_name like '%1%'
LOOP
EXECUTE FORMAT('select count(*) into' || tc 'from' || tb_name);
END LOOP
What should be the data type of tb_name and tc in this case?
CREATE OR REPLACE FUNCTION myfunc(_tbl_pattern text, _schema text = 'public')
RETURNS void AS -- or whatever you want to return
$func$
DECLARE
_tb_name information_schema.tables.table_name%TYPE; -- currently varchar
_tc bigint; -- count() returns bigint
BEGIN
FOR _tb_name IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = _schema
AND table_name ~ _tbl_pattern -- see below!
LOOP
EXECUTE format('SELECT count(*) FROM %I.%I', _schema, _tb_name)
INTO _tc;
-- do something with _tc
END LOOP;
END
$func$ LANGUAGE plpgsql;
Notes
I prepended all parameters and variables with an underscore (_) to avoid naming collisions with table columns. Just a useful convention.
_tc should be bigint, since that's what the aggregate function count() returns.
The data type of _tb_name is derived from its parent column dynamically: information_schema.tables.table_name%TYPE. See the chapter Copying Types in the manual.
Are you sure you only want tables listed in information_schema.tables? Makes sense, but be aware of implications. See:
How to check if a table exists in a given schema
a_horse already pointed to the manual and Andy provided a code example. This is how you assign a single row or value returned from a dynamic query with EXECUTE to a (row) variable. A single column (like count in the example) is decomposed from the row type automatically, so we can assign to the scalar variable tc directly - in the same way we would assign a whole row to a record or row variable. Related:
How to get the value of a dynamically generated field name in PL/pgSQL
Schema-qualify the table name in the dynamic query. There may be other tables of the same name in the current search_path, which would result in completely wrong (and very confusing!) results without schema-qualification. Sneaky bug! Or this schema is not in the search_path at all, which would make the function raise an exception immediately.
How does the search_path influence identifier resolution and the "current schema"
Always quote identifiers properly to defend against SQL injection and random errors. Schema and table have to be quoted separately! See:
Table name as a PostgreSQL function parameter
Truncating all tables in a Postgres database
I use the regular expression operator ~ in table_name ~ _tbl_pattern instead of table_name LIKE ('%' || _tbl_pattern || '%'), that's simpler. Be wary of special characters in the pattern parameter either way! See:
PostgreSQL Reverse LIKE
Escape function for regular expression or LIKE patterns
Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL
I set a default for the schema name in the function call: _schema text = 'public'. Just for convenience, you may or may not want that. See:
Assigning default value for type
Addressing your comment: to pass values, use the USING clause like:
EXECUTE format('SELECT count(*) FROM %I.%I
WHERE some_column = $1', _schema, _tb_name,column_name)
USING user_def_variable;
Related:
INSERT with dynamic table name in trigger function
It looks like you want the %I placeholder for FORMAT so that it treats your variable as an identifier. Also, the INTO clause should go outside the prepared statement.
FOR tb_name in select table_name from information_schema.tables where table_schema='some_schema' and table_name like '%1%'
LOOP
EXECUTE FORMAT('select count(*) from %I', tb_name) INTO tc;
END LOOP

Strategy of updating multiple tables in postgresql

I am new to postgresql and I need help on strategy on when a large processing is being executed.
I have table which holds invoices, The records in this invoice table need to be 'posted' to multiple tables say sales table and also income table.
This invoice table will be access by multiple users at the same time and when a user 'post' a particular record in the invoice table I want to prevent other users from making changes and 'posting' until the 'posting' is finished by the first user. How should i do it properly? Should I wrap the 'posting' in a transaction?
Thanks
Thanks Stepel and Landa for replying and sorry for not being clear.
Here's the coding that I am currently using in Foxpro.
Select InvHeader
If !Rlock()
Return
EndIf
Select InvDetail
Scan
do processing and verification
..
Insert Into tAr ... &&& temporary AR table
Insert Into tGl ... &&& temporary GL table
EndScan
*** the reason I am using temporary table is that at this stage, the use may
print out the detail of invoices that are to be posted and can then decide
whether to proceed to commit the transaction.
Select InvHeader
Replace InvHeader.Posted With .T.
Select tAr
Scan
do processing ...
Insert Into Ar (....
EndScan
Select tGl
Scan
do processing ...
Insert Into GL ( ...
EndScan
Begin Transaction
Select Ar
If !TableUpdate()
Rollback
Return
EndIf
Select Gl
If !TableUpdate()
Rollback
Return
EndIf
Select InvHeader
If !TableUpdate()
Rollback
Return
EndIf
** everything is ok the commit
End Transaction
Return
Should I do this posting procedure in the server using plpgsql? Or a combination of Foxpro code and SqlExec() statements? Are there any better ways to accomplish this?
Thanks.
Yes, you should use transactions. In this case the most sensitive thing is id of invoice header. If you use
INSERT INTO invoice_header_table VALUES (..) RETURNING id;
you have unique id of invoice header.
I am not sure if it was your point actually but I hope this information will be usefull.

SQL queries to pull data from IBM i (AS400) - How to separate multiple queries

What is the query separator used in AS400 to run multiple statements in same SQL . We have GO statement in MS SQL similarly I though semicolon is used in AS400 to separate multiple queries but for some reason it’s not working.
I actually have multiple steps in this query
Crete a GLOBAL TEMPORARY TABLE
Insert the data in to this Global temp table
Some logic to update the data on this Global temp table by
joining with other table.
Select the value from this Global temp table
When I use semicolon to separate each step I get error saying semicolon is not recognized as query separator… Below is the snippet of the query
DECLARE GLOBAL TEMPORARY TABLE SESSION.FinalRes
(
SLSTRTY CHAR(4)
,SLSMAN CHAR(5)
,CSTNAM CHAR(30)
,CustN CHAR(16)
,ADR1 CHAR(30)
,ADR4 CHAR(30)
,"STATE" CHAR(2)
,ZIPCD CHAR(12)
,DTEADDED DATE
,SalesCM DECIMAL(22,7)
,SalesYTD DECIMAL(22,7)
,SalesPY DECIMAL(22,7)
,EXTGPCM DECIMAL(22,7)
,EXTGPYTD DECIMAL(22,7)
,EXTGPPY DECIMAL(22,7)
,GMYTD DECIMAL(22,7)
,GMCM DECIMAL(22,7)
,GMPY DECIMAL(22,7)
,SalesPYM DECIMAL(22,7)
,SalesPYTD DECIMAL(22,7)
,STATIND CHAR(1)
,CSTCLS CHAR(3)
,CSort NUMERIC(5)
) WITH REPLACE ON COMMIT PRESERVE ROWS ;
SELET * from SESSION.FinalRes
This is the error I get when I try to execute the query
SQL0104: Token ; was not valid. Valid tokens: END-OF-STATEMENT.
Cause . . . . . : A syntax error was detected at token ;. Token ;
is not a valid token. A partial list of valid tokens is
. This list assumes that the statement is correct
up to the token. The error may be earlier in the statement, but the
syntax of the statement appears to be valid up to this point. Recovery
. . . : Do one or more of the following and try the request again:
-- Verify the SQL statement in the area of the token ;. Correct the statement. The error could be a missing comma or quotation mark, it
could be a misspelled word, or it could be related to the order of
clauses. -- If the error token is , correct the SQL
statement because it does not end with a valid clause.
The DB2 for i SQL will prepare and execute [EXECUTE IMMEDIATE] only one dynamic statement per invocation. Although I have not had the opportunity to use the feature, since the DB2 for IBM i 7.1, there is apparently support for a Dynamic Compound Statement to be accepted. The statement separator [for both declarative and procedural statements] in a compound statement, is the semicolon.
http://www.itjungle.com/fhg/fhg011514-story02.html
Dynamic Compound Statements In DB2 For i
Published: January 15, 2014
by Michael Sansoterra
Probably this has already been accounted for, but your 'select' keyword is missing a 'c'. Also try putting an additional semicolon after the select statement.