Harbour API SQL

Fórum sobre Banco de Dados e RDDs para Clipper/[x]Harbour.

Moderador: Moderadores

Ranier
Usuário Nível 2
Usuário Nível 2
Mensagens: 80
Registrado em: 02 Abr 2019 09:01
Localização: Goiania/Goias

Harbour API SQL

Mensagem por Ranier »

Acredito que o grande sucesso que o Clipper teve foi devido a facilidade de se programar, aliado ao fato de se ter uma api uniforme para tratar do acesso de dados, relatórios e telas.
Com a evolução da própria informática, o Clipper ficou para trás, principalmente com os erros cometidos pela CA.
Então, a grande questão para todos, que facilitaria e unificaria o desenvolvimento dos sistemas em xBase, é ter uma api uniforme e que todos a usem.
Então, teria que seria criado na minha opnião, uma API SQL, que qualquer biblioteca seguisse e que para o desenvolvedor xBase seria única, então bastaria fazer um só fonte (prg) e ele funcionaria em qualquer ambiente que o Harbour fosse portado.
Parece-em que alguém têm uma proposta chamada HDO (Acesso Universal de Dados).
O autor está chamando essa api de RDL (Replaceable Data Link), poderia ser um caminho, se todos trilhassem juntos.
email do autor: mesanonimo@gmail.com

Código: Selecionar todos

/***
 * Proyecto: Harbour Data Objects (HDO)
 * Fichero: hdoRDL.c
 * Descripcion: Gestion de de RDLs
 * Autor: Manu Exposito 2014-19
 * Fecha: 07/07/2019
 */

/*
 * Notas:
 * Actualmente solo tiene una DATA de solo lectura con una tabla Hash
 * { NOMBRE_RDL => PUNTERO_FUNCION_METODOS }
 *
 * Al registrar el nodo deberian quedar resueltas las tablas virtuales de metodos
 *
 * Para el nuevo sistema se usara una estructura NODO -> LISTRDLNODE y NODOS -> RDLNODE
 * Crear por getNode o findRDL
 * Quitar METHOD( "GETPFUNCBYNAME",   TRDL_GETPFUNCBYNAME )
 * Crear getHDOMethods, getSTMTMethods y getRSMethods
 */

#include "hdordl.h"

/***
 * Definicion de la clase con atributos y metodos
 */

CREATE_CLASS( "TRDL", TRDL, TRDL_IVARCOUNT )
{
/* Metodos */
METHOD( "NEW",              TRDL_NEW )
METHOD( "REGISTER",         TRDL_REGISTER )
METHOD( "DEREGISTER",       TRDL_DEREGISTER )
METHOD( "LIST",             TRDL_LIST )
METHOD( "ISREGISTERED",     TRDL_ISREGISTERED )
METHOD( "GETPOS",           TRDL_GETPOS )
METHOD( "GETNAME",          TRDL_GETNAME )
}
END_CLASS

/***
 * Metodo: NEW
 * Uso: Constructor de la clase
 * Parametros: Ninguno
 * Devuelve: Self, un objeto inicializado
 */

HB_METHOD( TRDL_NEW )
{
PHB_ITEM pSelf = HB_PSELF();
PHB_ITEM pd = hdo_listRDL();

hb_arraySetForward( pSelf, IVAR_HASHRDL, pd );
HDO_ITEM_FREE( pd );

/* Devuelve SELF */
hb_itemReturnRelease( pSelf );
}

/***
 * Metodo: LIST
 * Uso: Consulta de RDL registrados
 * Parametros: Ninguno
 * Devuelve: Array con los RDLs registrados
 */

HB_METHOD( TRDL_LIST )
{
PHB_ITEM pd = HB_GETHRDL();

if( pd )
{
hb_itemReturnRelease( hb_hashGetValues( pd ) );
}
else
{
hb_reta( 0 );
}
}

/***
 * Metodo: REGISTER
 * Uso: Registra un RDL si no existe
 * Parametros: El nombre del RDL y un puntero a la funcion del RDL
 * Devuelve: Nada
 */

HB_METHOD( TRDL_REGISTER )
{
PHB_ITEM pd = HB_GETHRDL();

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );
PHB_ITEM pValue = hb_param( 2, HB_IT_STRING );

hb_hashAdd( pd, pKey, pValue );
}
}

/***
 * Metodo: REGISTER
 * Uso: Registra un RDL si no existe
 * Parametros: El nombre del RDL y un puntero a la funcion del RDL
 * Devuelve: Nada
 */

HB_METHOD( TRDL_DEREGISTER )
{
PHB_ITEM pd = HB_GETHRDL();
HB_BOOL fRet = HB_FALSE;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

fRet = hb_hashDel( pd, pKey );
}

hb_retl( fRet );
}

/***
 * Metodo: ISREGISTERED
 * Uso: Comprueba si un RDL esta registrado
 * Parametros: Nombre del RDL
 * Devuelve: Valor logico indicando si existe o no
 */

HB_METHOD( TRDL_ISREGISTERED )
{
PHB_ITEM pd = HB_GETHRDL();
HB_BOOL fRet = HB_FALSE;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

fRet = ( hb_hashGetCItemPos( pd, hb_itemGetCPtr( pKey ) ) != 0 );
}

hb_retl( fRet );
}

/***
 * Metodo: GETPOS
 * Uso: Comprueba la posicion que ocupa un RDL
 * Parametros: Nombre del RDL
 * Devuelve: La posicion o 0 encaso de no estar registrado
 */

HB_METHOD( TRDL_GETPOS )
{
PHB_ITEM pd = HB_GETHRDL();
HB_SIZE nRet = 0;

if( pd )
{
PHB_ITEM pKey = hb_param( 1, HB_IT_STRING );

nRet = hb_hashGetCItemPos( pd, hb_itemGetCPtr( pKey ) );
}

hb_retni( nRet );
}

/***
 * Metodo: GETNAME
 * Uso: Consultar el nombre de un RDL por la posicion que ocupa
 * Parametros: Entero positivo
 * Devuelve: El nombre del RDL si esta registrado o NULL si no lo esta
 */

HB_METHOD( TRDL_GETNAME )
{
PHB_ITEM pd = HB_GETHRDL();
PHB_ITEM pRet = NULL;

if( pd )
{
HB_USHORT nPos = hb_parni( 1 );

if( nPos >= 1 )          /* Controlar el LEN del array */
{
pRet = hb_hashGetKeyAt( pd, nPos );
}
}

if( pRet )
{
hb_itemReturn( pRet );
}
else
{
hb_retc_null();
}
}
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Não entendi a diferença dela para o SQLMIX.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Ranier
Usuário Nível 2
Usuário Nível 2
Mensagens: 80
Registrado em: 02 Abr 2019 09:01
Localização: Goiania/Goias

Harbour API SQL

Mensagem por Ranier »

Não conheço a proposta HDO a fundo para responder 100%.
Sqlmix, pelo que sei, você tem mais conhecimentos.
Mas essa sintaxe ai:
? RDDINFO(RDDI_CONNECT,{"ODBC","Server=.\SQLEXPRESS;Driver={SQL Server};dsn=;Uid=sa;database=test;pwd=pass" },'SQLMIX' )
? RDDINFO(RDDI_EXECUTE, "DROP TABLE test")
? RDDINFO(RDDI_EXECUTE, "CREATE TABLE test (CODE char(3), DESCR char(50), VALUE int(11))")
? RDDINFO(RDDI_EXECUTE, "INSERT INTO test values ('AA','Lithuania', 3369600), ('USA', 'United States of America', 305397000)")

Esse é o problema maior, se tiver uma api unificada e util, cada rdd poderia se adaptar e cada um usaria a que achasse melhor. Exemplo:
dbQuery(), é um ótimo nome de função que é bem explicado. Executa uma "querie" que traz um dataset de retorno.
dbExecute(), executa uma "querie" que não traz um dataset de retorno.

Essas divisões, que existem, existem porque não existem uma api unificada para tudo no mundo xBase.
Se o Harbour adotasse uma api, para tudo, seria o melhor para todo o mundo xBase.
Igual foi feito no Vulkan, é uma proposta de API, cada um tem que seguir as diretrizes la, para fazerem os drivers.
Quem usa os drivers, não se importa com a especificação em si.

Se o Harbour criar uma API SQL, uma nova RDD, agora voltada para o mundo SQL, sem trazer os problemas do mundo DBF.
SQLMix, SQLRDD, poderiam adota-la. Assim, cada uma faria a sua escolha, mas teria apenas um fonte.
Não precisaria mudar no futuro, tudo o que ja foi feito, de novo, e de novo.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Me desculpe, mas ainda não entendi. Essa mesma sintaxe funciona para qualquer motor SGBD.
O RDD do harbour é flexível, ao ponto de transformar a resposta em DBF, na memória, pois estamos acostumados. E os comandos são em SQL quando formos interagir com os SGBD´s e com a nossa aplicação usamos sintaxe xBase.
Esses comandos em SQL do SQLMIX, funciona com qualquer SGBD. Não precisa mudar nada quando trocar de MySQL para Firebird por exemplo.
A proposta do SQLMIX eu consigo entender. Porém essa do HDO não entendi ainda.

Eu entendo alguma coisa do RDD do Harbour, pois já li alguns códigos dele, eu não sei se vc está se referindo a isso.

Código: Selecionar todos

                    Simple SQL Interface for Harbour



1. Introduction

   Simple SQL interface implements accessing SQL query result via RDD
interface. It is not intended to be replacement for "transparent" move of
DBFCDX application to SQL world.

   I want to discuss this in more detail. Many current RDDs for SQL servers
(ex. SQLRDD from xHarbour.com) tries to make a feeling you are working with
DBF file, but not with SQL database. SQL server does not support many
features, ex. RecNo(), deleted flag, file locks, record locks. These RDDs
are emulating these features to make feeling of DBF. Deleted() function is
emulated by creating additional table columns to store delete flag. Some
"hidden system" tables are used to register locking operations and emulate
record and file locks in DBF style. The idea of SQL query is also lost. If
you do a simple loop

 dbUseArea( , "select * from my_table" )
 DO WHILE ! Eof()
    somefunc( FIELD->some_sql_field )
    dbSkip()
 ENDDO

RDD usualy will read SQL rows in portions, let's say 100 records per query.
So, hidden queries are generated. If you are using indexes these queries
are really complicated. Let's have index on FIELD1 + Str( FIELD2 ). A seek
to value cValue1 + Str( nValue2 ) will generate a query like:

 SELECT * FROM my_table
     WHERE (FIELD1 == cValue1 and FIELD2 >= nValue2) or FIELD1 > cValue1
     ORDER BY FIELD1, FIELD2, _RECNO
     LIMIT 100

After evaluation of first 100 cached records, next query will be generated:

 SELECT * FROM my_table
     WHERE (FIELD1 == cLastField1 and FIELD2 == nLastValue2 and _RECNO > nLastRecno) or
           (FIELD1 == cLastField1 and FIELD2 > nLastValue2) or
           FIELD1 > cLastValue1
     ORDER BY FIELD1, FIELD2, _RECNO
     LIMIT 100

To optimize these queries the SQL index expresion should be
"FIELD1,FIELD2,_RECNO", but not "FIELD1,FIELD2" as written in INDEX ON
command.

   "Simple SQL interface" is too long to repeat every time I want to
address this library. I'll also use acronym "SSI" to address it.

   The idea of SSI is different. It does not make hidden queries. All
queries should be made explicitly by programmer. SSI gives access to query
result via RDD interface, it does not tries to emulate DBF and be
"plug-and-play" solution for DBF to SQL migration. If you do

 dbUseArea( , "select * from my_table")

all query (it could contain millions of records!) will be cached.

   The features of SSI approach are:

- It's possible to access SQL database of other applications. Other
 applications usualy does not follow agreement of "plug-and-play" SQL drivers
 about additional DELETED column, _RECNO in the end of index expression, etc.
 Access of SQL database of other applications is sometimes not possible.

- It's query oriented. That means a simple DO WHILE ! Eof() loop will iterate
 each records once and only once. This is not true for "plug-and-play" SQL
 drivers, if indexing is used. Just like in the case of loop over DBF file.
 It is not guaranteed that all records are included! Yes! If key value of the
 first record in index is changed to be the last record in index during the
 phase of record processing, DO WHILE ! Eof() loop will iterate only this
 single records even if the database contains millions of records. Your sould
 do FLock() on DBF to guarantee the records are not changed. Do you use FLock()
 before readonly DO WHILE ! Eof() loops? :)



2. Architecture


             +-------------+
             |             |
             | SQLMIX RDD  |
             |             |
             +-------------+
                  |  ^
                  V  |
             +-------------+    +---------+
             |             |--->|         |
             | SQLBASE RDD |    |   SDD   |
             |             |<---|         |
             +-------------+    +---------+


   SQLBASE RDD implements basic functionality for accessing SQL query result
via RDD interface. This RDD could be used, if indexing of query result is not
necessary or all indexing is done by SQL server (by using ORDER BY clause).

   SQLMIX RDD implements indexing of query result. This indexing is not
related to SQL server ORDER BY clause. SQLMIX do indexing of the query on the
client side.

   SDD is acronym for Sql Database Driver. RDD is used to implement access
of different database formats like DBF, SDF, etc. SDD is used to implement
access of different SQL databases. Every SQL server (MySQL, PostgreSQL, etc.)
has a corresponding SDD. SDD driver implements a specific part of data
exchange interface between SQLBASE and SQL server.



3. Modifying database

   SSI presents a query result via RDD interface and generates no hidden
SQL queries. So, how database can be changed? Does dbAppend() and FieldPut()
works, or is it readonly SQL interface?
   dbAppend(), FieldPut() and other similiar functions work on cached query
result, i.e. query can be appended by new rows and field values can be
changed, but SQL database is not changed. dbCreate() function can also be
used to create an "empty query result" but no table is created on SQL server.
So, SSI can also be used as implementation of "array RDD".
   The programmer must call SQL command explicitly to modify SQL tables.
SSI provides a method to detect which cached rows was changed or appended.
Essa é a proposta do SQLMIX.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
SQLMix seria outra coisa, inclusive é bem limitada, servindo basicamente para queries de consulta e nada mais.
Não. Veja nos fontes, que usamos para qualquer comando SQL.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Harbour API SQL

Mensagem por JoséQuintas »

hazael escreveu:É justamente isso que queria dizer, que SQLMix é muito bom, prático, mas não serve para nada além de consultas (ou relatórios).
Não tem como incluir, apagar, alterar, ou seja, não serve.
Só serve pra tirar proveito de velocidade no que SQL é bom kkkkkkk

Na prática, incluir/alterar/etc. é onde mais existe diferença entre as bases de dados, por isso complica.

Mesmo no ADO, isso tem limitações, porque os fabricantes geralmente criam coisas "usáveis", e não 100% funcionais.

Código: Selecionar todos

Clientes.Codigo = varCodigo
Clientes.Update
Isso pode dar erro, pelo simples fato do fabricante não ter conseguido (ou não ser possível) criar algo padrão.
Se fabricante não consegue... o que dizer dos "não fabricantes".

Aí... parte-se pra uso de SQL pra tudo, que sempre funciona e pronto.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Ranier
Usuário Nível 2
Usuário Nível 2
Mensagens: 80
Registrado em: 02 Abr 2019 09:01
Localização: Goiania/Goias

Harbour API SQL

Mensagem por Ranier »

Então, é isso que acontece, confusão na mente de todo mundo. SQLmix, ainda está mesclando dois mundos DBF e SQL. Parece-me que ele usa o dataset de retorno e emula alguns comandos dbf para o usuário.
Acredito que para quem puder é melhor migrar tudo para o SQL e com uma sintaxe nova e clara, de fácil compreensão.
Isso tudo é muito teórico, claro, mas seria o melhor para o xBase.

Se todos as libs adotasse essa sintaxe, por exemplo:

Código: Selecionar todos

res := dbQuery("SELECT * FROM Clientes ORDER BY Nome;")
Não importaria, se uso ADO, SQLMix, SQLRDD, HBDBD, teria que funcionar.

Poderia ser acrescentado parâmetros, para definir o modo de retorno, se como arrays ou hashs.
O que importa é unificar a API. Não conheço 100% da proposta do HDO e a usei apenas como exemplo, de ponto de partida.
Ranier
Usuário Nível 2
Usuário Nível 2
Mensagens: 80
Registrado em: 02 Abr 2019 09:01
Localização: Goiania/Goias

Harbour API SQL

Mensagem por Ranier »

Vendo esse exemplo em SQLMix:

Código: Selecionar todos

cRdd  := RDDSETDEFAULT( "SQLMIX" )
cAttributes:="Server=;Database=127.0.0.1:c:\temp\dbafirebird.gdb;User=SYSDBA;Password=masterkey;Driver={Firebird/InterBase(r) driver};"
nConn := RDDINFO( RDDI_CONNECT,{"ODBC",cAttributes} )

IF nConn == 0
   hwg_Msginfo( "Não conectado ao servidor: " + str(RDDINFO( RDDI_ERRORNO )) + hb_eol() + RDDINFO( RDDI_ERROR ) )
   RDDSETDEFAULT( cRdd )
   RETURN
ENDIF

DBUSEAREA( .T.,"SQLMIX", "select * from produto", "xx" )
Mescla, mundo DBF com SQL, criando e ocupando WorkAreas do RDD.
Agora veja em HBDBD:

Código: Selecionar todos

cCONN := "host='192.168.15.2' dbname='dbtests' user='postgres' password='xxxxxxx'"
hDBD := DBD_NEW(DBD_PGSQL_DRIVER)
If ( hDBD == NIL )
    hwg_Msginfo( "Sem memoria!")
    Return 1
Endif
nRET := DBD_OPEN( hDBD, sCONN )
If ( nRET != 0 )
    hwg_Msginfo( "Não conectado ao servidor: " + str(DBD_ERRORNO())
    Return nRET
Endif
hRES := DBD_QUERY( hDBD, "select * from produto" )
Nesse caso, não existe nenhum uso da RDD do Harbour, ou seja, não se têm uso de nenhum função que emule dbf.
É tudo em SQL e funções nativas da lib (HBDBD). O dataset hRES contém, todo o conjunto retornado pelo SGDB, que corresponde a instrução SQL solicitada. A partir dai, o uso dos dados, é intrínseco a HBDBD, que tem que usar funções e sintaxe própria.

Código: Selecionar todos

hRES := DBD_QUERY( hDBD, "select * from produto" )
If ( hRES != NIL) 
   hROW := DBD_FETCH_ROW( hDBD, hRES )
   While(( hROW := DBD_FETCH_ROW( hDBD, hRES ) ) != NIL )
       aROW := DBD_ROW_ARRAY( hDBD, hROW )
   Enddo
Endif
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Não sinta-se mal mas creio que afirmar assim com tanta convicção sem ter comprovação acaba causando confusão.
Eu hein... Tá nos exemplos, já testei n vezes, já ia sair do DBF usava o Letodb, mas tinha bugs... Ai o Elch fez o LetoDbf.
Eu não sei pq vc estão afirmando que o SQLMIX só extrai dados. Eu hein. Não vou ficar aqui evangelizando, veja vcs mesmo nos exemplos.
CRIAR, EXCLUIR, INCLUIR etc. e tals , qualquer comando SQL... Valei me... Só faltam dizer que a terra é plana!
Não entendeu o texto em inglês do Mindaugas... Ele diz que para interagir com o SGBD só com SQL.
Olha os comandos ai:

Código: Selecionar todos

 RDDINFO(RDDI_CONNECT,{"ODBC","Server=.\SQLEXPRESS;Driver={SQL Server};dsn=;Uid=sa;database=test;pwd=pass" },'SQLMIX' )
? RDDINFO(RDDI_EXECUTE, "DROP TABLE test")
? RDDINFO(RDDI_EXECUTE, "CREATE TABLE test (CODE char(3), DESCR char(50), VALUE int(11))")
? RDDINFO(RDDI_EXECUTE, "INSERT INTO test values ('AA','Lithuania', 3369600), ('USA', 'United States of America', 305397000)")
Ele não fez e nem deseja um EMULADOR de DBF.

Código: Selecionar todos

SDD gives you an access to query result. So, you edit query result but not database itself. Editing of database are not possible in all cases, f.e., you can not edit it if it has no primary key. So, editing put an additional restrictions on databases, or some "hidden" columns if you expect SDD to be able write to database. That was not my goal to emulate .dbf in SQL, because you'll never can emulate 100% of behavior. The more you emulate (locks, deleted records, etc) the less real SQL capabilities you have (you start to loose atomic database operations, etc). My goal was not to emulate everything and have .dbf features in SQL databases, without having SQL features.

The solution
============
I'm using functions like SQLInsert("table", {"FIELD"=>xValue, ...}) and SQLUpdate("table", {"FIELD"=>xValue, ...}, {"ID"=>xPrimaryKey}). It composes SQL query string and calls RDDINFO(RDDI_EXECUTE, cQuery). These functions are very easy to implement, ex:
------------
STATIC FUNC SQLUpdate(pcTable, phSource, pchWhere)
 LOCAL aI := {}, aJ, xVal

 FOR EACH xVal IN phSource
   SWITCH VALTYPE(xVal)
   CASE "C"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, SQLValueToString(xVal))) ; EXIT
   CASE "D"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, DTOS(xVal)))             ; EXIT
   CASE "N"; AADD(aI, HB_StrFormat("%s=%d", xVal:__enumKey, xVal))                ; EXIT
   CASE "T"; AADD(aI, HB_StrFormat("%s=%s", xVal:__enumKey, LEFT(HB_TTOS(xVal), 14))); EXIT
   CASE "U"; AADD(aI, HB_StrFormat("%s=NULL", xVal:__enumKey))                ; EXIT
   END
 NEXT
 IF HB_IsHash(pchWhere)
   aJ := {}
   FOR EACH xVal IN pchWhere
     SWITCH VALTYPE(xVal)
     CASE "C"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, SQLValueToString(xVal))) ; EXIT
     CASE "D"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, DTOS(xVal)))             ; EXIT
     CASE "N"; AADD(aJ, HB_StrFormat("%s=%d", xVal:__enumKey, xVal))                  ; EXIT
     CASE "T"; AADD(aJ, HB_StrFormat("%s=%s", xVal:__enumKey, LEFT(HB_TTOS(xVal), 14))); EXIT
     CASE "U"; AADD(aJ, HB_StrFormat("%s IS NULL", xVal:__enumKey))                  ; EXIT
     END
   NEXT
   pchWhere := join(aJ, " AND ")
 ENDIF
RETURN RDDINFO(RDDI_EXECUTE, HB_StrFormat("UPDATE %s SET %s WHERE %s", pcTable, join(aI, ","), pchWhere))
------------

But I've never tried to upload these to SVN, because these functions are also limited. I'm also not sure that people likes storing record in hash. It also a separate thing, it is not related to SDD.


Regards,
Mindaugas
Apenas a QUERY retorna em um padrão DBF, mas para para atualizar o SGDB vc usa: "RDDINFO(RDDI_EXECUTE,comando SQL"
Não entenderam até hoje o conceito.
Lá no passado quando testei funcionou. Criei, incluir, alterei... usando RDDINFO(RDDI_EXECUTE...

Exemplo de uso do povo da Minigui!

Código: Selecionar todos

* RDD SQL DEMO
* Based on Harbour Compiler Contrib Samples
* Adapted for HMG by Roberto Lopez - 2009

* Modified 2009.11.13 by Marek Olszewski "MOL" for testing Record Locking with MySQL

#include "minigui.ch"
#include "dbinfo.ch"
#include "error.ch"

#define DBI_QUERY             1001

#define RDDI_CONNECT          1001
#define RDDI_DISCONNECT       1002
#define RDDI_EXECUTE          1003
#define RDDI_ERROR            1004
#define RDDI_ERRORNO          1005
#define RDDI_NEWID            1006
#define RDDI_AFFECTEDROWS     1007
#define RDDI_QUERY            1008


REQUEST SDDMY, SQLMIX



Function Main
LOCAL hConn

	RDDSETDEFAULT("SQLMIX")

	IF RDDINFO(RDDI_CONNECT, {"MYSQL", "localhost", "root","123", "TEST"}) == 0
		MSGSTOP("Unable connect to the server")
		RETURN 
	ENDIF

	CreateTable()

	// it's required to use record locking to set autocommit to 0!!!!
	RDDINFO(RDDI_EXECUTE,"SET AUTOCOMMIT = 0")
	DBUSEAREA( .T.,, "SELECT * FROM country", "country" )

	DEFINE WINDOW MAIN ;
		ROW 0 ;
		COL 0 ;
		WIDTH 600 ;
		HEIGHT 500 ;
		TITLE "RDD SQL - TESTING ROW LOCKING MODIFIED BY 'MOL" ;
		WINDOWTYPE MAIN  

		DEFINE BROWSE browse1
			ROW 		40
			COL 		10
			WIDTH		470			
			HEIGHT		330
			HEADERS 	{'Code','Name','Residents'}
			WIDTHS		{135,155,135}
			WORKAREA	COUNTRY
			FIELDS		{'Country->Code','Country->Name','Country->Residents'}
		END BROWSE
		DEFINE BUTTON B1
			ROW 10
			COL 10
			WIDTH 100
			CAPTION "REFRESH"
			ACTION REFRESH_BROWSE()
		END BUTTON

		DEFINE BUTTON B_LOCK
			ROW 10
			COL 120
			WIDTH 100
			CAPTION "LOCK 'POL'"
			ACTION LOCK_RECORD()
		END BUTTON
		
		DEFINE BUTTON RELEASE_LOCK
			ROW 10
			COL 230
			WIDTH 100
			CAPTION "RELEASE LOCK"
			ACTION RELEASE_lock()
		END BUTTON
		DEFINE BUTTON MODIFY_POL
			ROW 10
			COL 340
			WIDTH 100
			CAPTION "MODIFY 'POL'"
			ACTION CHANGE_RECORD()
			
		END BUTTON
			
	END WINDOW

	MAIN.Center

	MAIN.Activate

Return

************************
STATIC PROC CreateTable()
   ? RDDINFO(RDDI_EXECUTE, "DROP TABLE country")
   ? RDDINFO(RDDI_EXECUTE, "CREATE TABLE country (CODE char(3), NAME char(50), RESIDENTS int(11))")
   ? RDDINFO(RDDI_EXECUTE, "INSERT INTO country values ('LTU', 'Lithuania', 3369600), ('USA', 'United States of America', 305397000), ('POR', 'Portugal', 10617600), ('POL', 'Poland', 38115967), ('AUS', 'Australia', 21446187), ('FRA', 'France', 64473140), ('RUS', 'Russia', 141900000)")
RETURN

************************
function REFRESH_BROWSE
	DBUSEAREA( .F.,, "SELECT * FROM country", "country" )
	MAIN.BROWSE1.REFRESH
	MAIN.BROWSE1.SETFOCUS
return
************************
PROC RDDSYS();  RETURN
************************
FUNCTION LOCK_RECORD
	IF RDDINFO(RDDI_EXECUTE,"SELECT * FROM country WHERE CODE='POL' FOR UPDATE")
		MsgBox("Locked...")
	ELSE
		MsgBox("it's not possible to lock record...")
	ENDIF
return
************************
FUNCTION RELEASE_LOCK
	RDDINFO(RDDI_EXECUTE,"COMMIT")
return
************************ 
FUNCTION CHANGE_RECORD
	local cNewName := ""

	RDDINFO(RDDI_EXECUTE,"COMMIT;")
	WAIT WINDOW "WAITING FOR LOCK RECORD IN DATABASE..."
	IF RDDINFO(RDDI_EXECUTE,"SELECT * FROM country WHERE CODE='POL' FOR UPDATE")
		
		cNewName := AllTrim ( InputBox ( "Enter new name for CODE='POL'" , "Enter new name...") )
		RDDINFO(RDDI_EXECUTE,"update country set name = '" + cNewName +"' where CODE ='POL'")
		RDDINFO(RDDI_EXECUTE,"COMMIT")
	ELSE
		MSGSTOP("I can't lock record for CODE='POL'")
	ENDIF
	WAIT CLEAR
	REFRESH_BROWSE()
return
************************
function xtoC
	param	xValue
	local ret
	
	if type("xValue") == "D"
		ret := dtoc(xValue)
	elseif type("xValue") == "N"
		ret := str(xValue)
	elseif type("xValue") == "L"
		ret := if(xValue,"True","false")
	elseif type("xValue") = "C"
		ret := xValue
	else
		ret := ""
	endif
 return ret
Quem tem que testar é o interessado, quando eu precisei testei e funcionou, agora testem e mostrem que não faz, porque tudo é trabalho.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Harbour API SQL

Mensagem por JoséQuintas »

Ranier escreveu:Se todos as libs adotasse essa sintaxe, por exemplo:
Ao mesmo tempo, faz sentido, e não faz sentido....
Mais padronizado que ODBC e ADO impossível.
ADO só tem pra Windows, mas ODBC tem pra Linux também.

Se já existe padrão ADO e padrão ODBC.... um novo padrão vai ser algo não padrão kkkkk

E voltamos à estaca zero, qualquer novo padrão não vai ser padrão enquanto não for seguido.

Esse é um grande problema no xbase.....
Todo mundo à espera de um padrão que não existe...
E conforme aparece alguma coisa, se torna fora do padrão, e lá se vão mais usuários pra fora do padrão.

Pra mim não tem que inventar um novo padrão, é seguir algum dos que já existem, que acabam virando padrão.

Regra básica:
Se nem a porr. do compilador C, não seguem a padronização, o que dizer de acesso a base de dados?
Quanto mais opções no Harbour pior !!!! infelizmente.

Conclusão:
Não existe padrão.
Não tem como definir um padrão.
Mas... o SQLMIX não é um padrão?
Se não serve pra padrão.... então ninguém aceita um padrão, vamos perder tempo definindo padrão que não será usado?
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Só serve pra tirar proveito de velocidade no que SQL é bom kkkkkkk

Na prática, incluir/alterar/etc. é onde mais existe diferença entre as bases de dados, por isso complica.
Quintas já coloquei n vezes aqui no forum... Mas no seu pensamento só existe ADO, infelizmente existe pessoas que só usam LINUX e o pessoal do Harbour os mestre tem como padrão o LINUX como SO.
Se é melhor se é pior eu não sei. Eu sei que podemos incluir, excluir, etc.. usando SQLMIX.
cQuery := "bla, bla, bla..."

RDDINFO(RDDI_EXECUTE, "update" + cQuery )
cQuery := "bla, bla, bla..."
RDDINFO(RDDI_EXECUTE, "insert" + cQuery )

https://hmgthinking.wordpress.com/2015/07/18/23/
V. YES!

Yes indeed: The remedy was worse than the disease, but not for technical reasons, but, personal ones.

I’ve successfully ported one module of the app to MySql, using SQLMIX RDD.

It worked perfect (technically speaking, at least) but something bothered me…

In fact… two things…

I love the standalone/portable EXEs: it is one of the sexiest features on Harbour, but MySql requires a dll.
SQL itself: SQL is great… I know… You can do everything with it. The problem is that I don’t feel comfortable with it. My brain, my heart and the rest of my body started to tell me: ‘Hey… SQL is not as easy, intuitive and enjoyable as xBase is… please… STOP THIS!

Then… I’ve done it… I’ve stopped the migration process and started to think again.

VI. A FUNNY THING HAPPENED IN THE WAY TO… CLIENT/SERVER

And I’ve remembered a new Harbour toy called NETIO.

It, basically, could allow you to use your actual code, designed for local access, on a LAN or even through the Internet with minor changes.
Ele achou o NetIO eu achei o LetoDbf, estamos felizes com DBF.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Mas... o SQLMIX não é um padrão?
Sim lógico que é. Qual é o comando novo ? São comandos padrões do xBase e dos SGBDs -> SQL.
O problema é a adoção e pessoas para ensinar.

Tem outra pessoa muito gabaritada que fez um RDD "pra tudo" e o povo nem comentou, agora ele já está criando um drive do harbour para MongoDB.
Teo Fonrouge
Harbour OORDB is a Object Oriented Relational Database
https://github.com/tfonrouge/oordb
https://medium.com/harbour-magazine/int ... 9a1a167f7b

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Ranier
Usuário Nível 2
Usuário Nível 2
Mensagens: 80
Registrado em: 02 Abr 2019 09:01
Localização: Goiania/Goias

Harbour API SQL

Mensagem por Ranier »

"Ele achou o NetIO eu achei o LetoDbf, estamos felizes com DBF."
Sim, muito bom. Eu entendo as dores da mudança. Passei por isso faz tempo.
Mas no meu entendimento, dbf não é o futuro, infelizmente. Claro que vai ter sempre algum projeto usando.
Ele foi o melhor para isso por muito tempo.
Agora um projeto sério de ERP, que precise escalar, ir pra Web, mobile, BI, e acrescente todas as tecnologias novas por ai, vai precisar de um SGDB, não tenho dúvida.

"Agora, tem outra questão, o melhor mesmo é acesso nativo, direto, e se alguém conseguir fazer rotinas de acesso direto que não mudem de acordo com o tipo de SQL em uso, seria o ideal, mas teria que ser muito, muito simples e ao mesmo tempo não faltar nenhuma possibilidade ou recurso. Tipo como fucniona com JDBC, inclusive essa é uma proposta antiga do Viktor."
Acredito que consegui com o HDBD, nativo e direto, multibanco, multiplataforma.
Sem ODBC.
Agora, com ele a mudança seria grande, realmente.
Browse somente em arrays.
Sem replace, ou append blank.
Mudou algo no banco, nova consulta, mas é assim pra todo mundo que usa SQL.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Agora, tem outra questão, o melhor mesmo é acesso nativo, direto, e se alguém conseguir fazer rotinas de acesso direto que não mudem de acordo com o tipo de SQL em
A melhor proposta que já vi, foi o SQLMIX. É padronizado para o programador xBase. Traz o resultado em DBF, isso é fantástico, já poupa centenas de linhas.
As rotinas de GRUD, fazemos usando SQL que é padrão. Não sei onde está a falta de padrão.
Eu sei que tem tanto trabalho pronto, pq para quem ama PgSQL, vai querer usar o rdd do Rodrigo Moreno, e por ai vai...

Código: Selecionar todos

#require "hbpgsql"

PROCEDURE Main( cHost, cDatabase, cUser, cPass )

   LOCAL conn, res, x, y, pFile

   CLS

   conn := PQsetdbLogin( cHost, "5432", , , cDatabase, cUser, cPass )
   ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn )

   conn := PQconnectdb( ;
      "dbname = '" + hb_defaultValue( cDatabase, "postgres" ) + "' " + ;
      "host = '" + hb_defaultValue( cHost, "localhost" ) + "' " + ;
      "user = '" + hb_defaultValue( cUser, hb_UserName() ) + "' " + ;
      "password = '" + hb_defaultValue( cPass, "" ) + "' " + ;
      "port = 5432" )

   ? PQstatus( conn ), PQerrorMessage( conn )

   IF PQstatus( conn ) != CONNECTION_OK
      RETURN
   ENDIF

   ? "Blocking:", PQisnonblocking( conn ), PQsetnonblocking( conn, .T. ), PQisnonblocking( conn )

   pFile := PQtracecreate( hb_DirBase() + "trace.log" )
   PQtrace( conn, pFile )

   ? "Verbose:", PQsetErrorVerbosity( conn, 2 )

   ? "Protocol:", PQprotocolVersion( conn )
   ? "Server version:", PQserverVersion( conn )
   ? "Client encoding (old):", pg_encoding_to_char( PQclientEncoding( conn ) )
   ? "Set client encoding:", PQsetClientEncoding( conn, "UTF-8" )
   ? "Client encoding (new):", pg_encoding_to_char( PQclientEncoding( conn ) )

   ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn )

   res := PQexec( conn, "drop table products" )
   ? PQresultStatus( res ), PQresultErrorMessage( res )
   res := NIL

   res := PQexec( conn, "create table products ( product_no numeric(10), name varchar(20), price numeric(10,2) )" )
   ? PQresultStatus( res ), PQresultErrorMessage( res )

   res := PQexecParams( conn, "insert into products( product_no, name, price ) values ($1, $2, $3)", { "2", "bread", "10.95" } )
   ? "Oid Row:", PQoidValue( res ), PQoidStatus( res )

   IF PQresultStatus( res ) != PGRES_COMMAND_OK
      ? PQresultStatus( res ), PQresultErrorMessage( res )
   ENDIF

   res := PQexec( conn, 'select price, name, product_no as "produto" from products' )

   IF PQresultStatus( res ) != PGRES_TUPLES_OK
      ? PQresultStatus( res ), PQresultErrorMessage( res )
   ENDIF

   ? "Binary:", PQbinaryTuples( res )
   ? "Rows:", PQntuples( res )
   ? "Cols:", PQnfields( res )
   ? PQfname( res, 1 ), PQftable( res, 1 ), PQftype( res, 1 ), PQfnumber( res, "name" ), PQfmod( res, 1 ), PQfsize( res, 1 ), PQgetisnull( res, 1, 1 )

   FOR EACH x IN PQmetadata( res )
      ? "Line 1:", ""
      FOR y := 1 TO 6
         ?? x[ y ], ",", ""
      NEXT
   NEXT

   ? PQfcount( res )

   ? PQlastrec( res )

   ? PQgetvalue( res, 1, 2 )

   ? "Large Objects, always should be in a transaction..."

   PQexec( conn, "begin" )

   ? ( x := lo_import( conn, __FILE__ ) )
   ? lo_export( conn, x, hb_FNameExtSet( __FILE__, ".new" ) )
   ? lo_unlink( conn, x )

   PQexec( conn, "commit" )

   PQuntrace( conn )

   RETURN
Olhem a dificuldade de "labutar" com o resultado. Vem um texto enorme separado por vírgula.
Faço votos que achei coisa melhor, ou que criem coisa melhor, só estou tentando melhorar e imagem do SQLMIX e outros que estão na pasta contrib do Harbour.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Harbour API SQL

Mensagem por Itamar M. Lins Jr. »

Ola!
Agora, com ele a mudança seria grande, realmente.
Browse somente em arrays.
Sem replace, ou append blank.
Mudou algo no banco, nova consulta, mas é assim pra todo mundo que usa SQL.
Já é assim com SQLMIX. Traz a ARRAY em DBF, não tem nada salvo no HD. Depois que mexer, tem que salvar via comando SQL assim como se faz usando ADO.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Responder