github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/dboperation.go (about)

     1  // Copyright 2023 IAC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dbconn
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/mdaxf/iac/com"
    25  	"github.com/mdaxf/iac/engine/types"
    26  	"github.com/mdaxf/iac/logger"
    27  
    28  	_ "github.com/denisenkom/go-mssqldb"
    29  	_ "github.com/go-sql-driver/mysql"
    30  )
    31  
    32  type DBOperation struct {
    33  	DBTx       *sql.Tx
    34  	ModuleName string
    35  	iLog       logger.Log
    36  	User       string
    37  }
    38  
    39  // NewDBOperation creates a new instance of DBOperation.
    40  // It takes the following parameters:
    41  // - User: the name of the user performing the database operation.
    42  // - DBTx: the SQL transaction object.
    43  // - moduleName: the name of the module associated with the database operation.
    44  // If moduleName is empty, it defaults to logger.Database.
    45  // It returns a pointer to the newly created DBOperation instance.
    46  // The function also logs the performance duration of the operation.
    47  // If there is an error during the operation, it is recovered and logged as an error.
    48  // The function returns a pointer to the newly created DBOperation instance.
    49  
    50  func NewDBOperation(User string, DBTx *sql.Tx, moduleName string) *DBOperation {
    51  	startTime := time.Now()
    52  	if moduleName == "" {
    53  		moduleName = logger.Database
    54  	}
    55  	iLog := logger.Log{ModuleName: moduleName, User: User, ControllerName: "Database"}
    56  	defer func() {
    57  		elapsed := time.Since(startTime)
    58  		iLog.PerformanceWithDuration("dbconn.NewDBOperation", elapsed)
    59  	}()
    60  
    61  	return &DBOperation{
    62  		DBTx:       DBTx,
    63  		ModuleName: moduleName,
    64  		iLog:       iLog,
    65  		User:       User,
    66  	}
    67  }
    68  
    69  // Query executes a SQL query with optional arguments and returns the resulting rows.
    70  // It measures the performance of the query and logs any errors that occur.
    71  // If a database transaction is not already in progress, it begins a new transaction.
    72  // The transaction is committed if it was started locally.
    73  // The returned rows must be closed after use to release associated resources.
    74  // The function returns the following parameters:
    75  // - rows: the resulting rows.
    76  // - err: the error that occurred during the operation.
    77  // If there is an error during the operation, it is recovered and logged as an error.
    78  // The function returns the resulting rows and any error that occurred during the operation.
    79  // The function also logs the performance duration of the operation.
    80  
    81  func (db *DBOperation) Query(querystr string, args ...interface{}) (*sql.Rows, error) {
    82  
    83  	startTime := time.Now()
    84  	defer func() {
    85  		elapsed := time.Since(startTime)
    86  		db.iLog.PerformanceWithDuration("dbconn.Query", elapsed)
    87  	}()
    88  
    89  	defer func() {
    90  		if err := recover(); err != nil {
    91  			db.iLog.Error(fmt.Sprintf("There is error to query database with error: %s", err))
    92  			return
    93  		}
    94  	}()
    95  
    96  	db.iLog.Debug(fmt.Sprintf("Query: %s %s...", querystr, args))
    97  
    98  	idbtx := db.DBTx
    99  	blocaltx := false
   100  
   101  	if idbtx == nil {
   102  		idbtx, err = DB.Begin()
   103  		blocaltx = true
   104  		if err != nil {
   105  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   106  			return nil, err
   107  		}
   108  		defer idbtx.Commit()
   109  	}
   110  
   111  	//fmt.Println(string(args))
   112  	stmt, err := idbtx.Prepare(querystr)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	defer stmt.Close()
   117  
   118  	rows, err := stmt.Query(args...)
   119  
   120  	if err != nil {
   121  
   122  		idbtx.Rollback()
   123  
   124  		db.iLog.Error(fmt.Sprintf("There is error to query database with error: %s", err.Error()))
   125  		return nil, err
   126  	}
   127  	defer rows.Close()
   128  
   129  	if blocaltx {
   130  		idbtx.Commit()
   131  	}
   132  
   133  	return rows, nil
   134  }
   135  
   136  // QuerybyList executes a database query with a list of parameters.
   137  // It takes a query string, a list of parameter names, a map of parameter values, and a list of input types.
   138  // The function returns a map of query results, the number of rows affected, the number of rows returned, and an error (if any).
   139  // The function also logs the performance duration of the operation.
   140  // If there is an error during the operation, it is recovered and logged as an error.
   141  
   142  func (db *DBOperation) QuerybyList(querystr string, namelist []string, inputs map[string]interface{}, finputs []types.Input) (map[string][]interface{}, int, int, error) {
   143  	startTime := time.Now()
   144  	defer func() {
   145  		elapsed := time.Since(startTime)
   146  		db.iLog.PerformanceWithDuration("dbconn.QuerybyList", elapsed)
   147  	}()
   148  	defer func() {
   149  		if err := recover(); err != nil {
   150  			db.iLog.Error(fmt.Sprintf("There is error to query database with error: %s", err))
   151  			return
   152  		}
   153  	}()
   154  
   155  	db.iLog.Debug(fmt.Sprintf("Query: %s {%s} {%s}", querystr, namelist, inputs))
   156  
   157  	// create a slice to hold the parameter values in the same order as they appear in the SQL query
   158  	var values []interface{}
   159  
   160  	// Execute the SQL statement with the given inputs
   161  	for i := range namelist {
   162  		paramPlaceholder := "@" + namelist[i]
   163  		paramValuePlaceholder := ""
   164  		switch finputs[i].Datatype {
   165  		case types.Integer:
   166  			paramValuePlaceholder = fmt.Sprintf("%d", inputs[namelist[i]])
   167  		case types.Float:
   168  			paramValuePlaceholder = fmt.Sprintf("%f", inputs[namelist[i]])
   169  		case types.Bool:
   170  			paramValuePlaceholder = fmt.Sprintf("%t", inputs[namelist[i]])
   171  		default:
   172  			paramValuePlaceholder = fmt.Sprintf("'%v'", inputs[namelist[i]])
   173  		}
   174  		querystr = strings.Replace(querystr, paramPlaceholder, paramValuePlaceholder, -1)
   175  		//	values = append(values, inputs[namelist[i]])
   176  	}
   177  
   178  	idbtx := db.DBTx
   179  	blocaltx := false
   180  
   181  	if idbtx == nil {
   182  		idbtx, err = DB.Begin()
   183  		blocaltx = true
   184  		if err != nil {
   185  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   186  			return nil, 0, 0, err
   187  		}
   188  		defer idbtx.Commit()
   189  	}
   190  
   191  	var stmt *sql.Stmt
   192  
   193  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   194  	defer cancel()
   195  	stmt, err = idbtx.PrepareContext(ctx, querystr)
   196  	//stmt, err := idbtx.Prepare(querystr)
   197  	if err != nil {
   198  		db.iLog.Error(fmt.Sprintf("There is error to prepare the query: %s with error: %s", querystr, err.Error()))
   199  		return nil, 0, 0, err
   200  	}
   201  	defer stmt.Close()
   202  
   203  	rows, err := stmt.QueryContext(ctx, values...)
   204  	//	rows, err := stmt.Query(values...)
   205  	if err != nil {
   206  		idbtx.Rollback()
   207  		db.iLog.Error(fmt.Sprintf("There is error to execute the query: %s with error: %s", querystr, err.Error()))
   208  		return nil, 0, 0, err
   209  	}
   210  	defer rows.Close()
   211  
   212  	if blocaltx {
   213  		idbtx.Commit()
   214  	}
   215  
   216  	return db.Conto_JsonbyList(rows)
   217  }
   218  
   219  // Query_Json executes a database query with the provided query string and arguments,
   220  // and returns the result as a slice of maps, where each map represents a row of the result set.
   221  // The query is executed within a transaction, and the transaction is automatically committed
   222  // if it was initiated locally. If an error occurs during the query or conversion to JSON,
   223  // the transaction is rolled back and the error is returned.
   224  //
   225  // Parameters:
   226  // - querystr: The query string to execute.
   227  // - args: Optional arguments to be passed to the query.
   228  //
   229  // Returns:
   230  // - []map[string]interface{}: The result set as a slice of maps.
   231  // - error: Any error that occurred during the query or conversion to JSON.
   232  
   233  func (db *DBOperation) Query_Json(querystr string, args ...interface{}) ([]map[string]interface{}, error) {
   234  	startTime := time.Now()
   235  	defer func() {
   236  		elapsed := time.Since(startTime)
   237  		db.iLog.PerformanceWithDuration("dbconn.Query_Json", elapsed)
   238  	}()
   239  
   240  	defer func() {
   241  		if err := recover(); err != nil {
   242  			db.iLog.Error(fmt.Sprintf("There is error to query database with error: %s", err))
   243  			return
   244  		}
   245  	}()
   246  
   247  	db.iLog.Debug(fmt.Sprintf("Query with json object result: %s %s...", querystr, args))
   248  
   249  	idbtx := db.DBTx
   250  	blocaltx := false
   251  
   252  	if idbtx == nil {
   253  		idbtx, err = DB.Begin()
   254  		blocaltx = true
   255  		if err != nil {
   256  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   257  			return nil, err
   258  		}
   259  		defer idbtx.Commit()
   260  	}
   261  
   262  	//fmt.Println(string(args))
   263  	//stmt, err := idbtx.Prepare(querystr)
   264  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   265  	defer cancel()
   266  	stmt, err := idbtx.PrepareContext(ctx, querystr)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	defer stmt.Close()
   271  
   272  	rows, err := stmt.QueryContext(ctx, args...)
   273  	//rows, err := stmt.Query(args...)
   274  
   275  	if err != nil {
   276  		idbtx.Rollback()
   277  		db.iLog.Error(fmt.Sprintf("There is error to query database with error: %s", err.Error()))
   278  		return nil, err
   279  	}
   280  	defer rows.Close()
   281  
   282  	db.iLog.Debug(fmt.Sprintf("Query with json object result:%v...", rows))
   283  	jsondata, err := db.Conto_Json(rows)
   284  	if err != nil {
   285  		idbtx.Rollback()
   286  		db.iLog.Error(fmt.Sprintf("There is error to convert the rows to json with error: %s", err.Error()))
   287  		return nil, err
   288  	}
   289  
   290  	if blocaltx {
   291  		idbtx.Commit()
   292  	}
   293  
   294  	return jsondata, nil
   295  }
   296  
   297  // ExecSP executes a stored procedure with the given procedureName and arguments.
   298  // It measures the execution time and logs the performance.
   299  // If an error occurs during execution, it logs the error and rolls back the transaction.
   300  // If a local transaction is used, it commits the transaction at the end.
   301  // Parameters:
   302  //   - procedureName: the name of the stored procedure to execute
   303  //   - args: the arguments to pass to the stored procedure
   304  // Returns:
   305  //   - error: an error if there was a problem executing the stored procedure
   306  
   307  func (db *DBOperation) ExecSP(procedureName string, args ...interface{}) error {
   308  	startTime := time.Now()
   309  	defer func() {
   310  		elapsed := time.Since(startTime)
   311  		db.iLog.PerformanceWithDuration("dbconn.ExecSP", elapsed)
   312  	}()
   313  
   314  	defer func() {
   315  		if err := recover(); err != nil {
   316  			db.iLog.Error(fmt.Sprintf("There is error to execute store procedure %s in database with error: %s", procedureName, err))
   317  			return
   318  		}
   319  	}()
   320  	db.iLog.Debug(fmt.Sprintf("start execute the Store procedure: %s with parameters %s...", procedureName, args))
   321  
   322  	idbtx := db.DBTx
   323  	blocaltx := false
   324  
   325  	if idbtx == nil {
   326  		idbtx, err = DB.Begin()
   327  		blocaltx = true
   328  		if err != nil {
   329  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   330  			return err
   331  		}
   332  		defer idbtx.Commit()
   333  	}
   334  
   335  	// Construct the stored procedure call with placeholders for each parameter
   336  	placeholders := make([]string, len(args))
   337  	for i := range args {
   338  		placeholders[i] = "?"
   339  	}
   340  	call := fmt.Sprintf("CALL %s(%s)", procedureName, strings.Join(placeholders, ","))
   341  
   342  	db.iLog.Debug(fmt.Sprintf("Call the stored procedure %s with the dynamic parameters %s...", call, args))
   343  
   344  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   345  	defer cancel()
   346  
   347  	// Call the stored procedure with the dynamic parameters
   348  	_, err := idbtx.ExecContext(ctx, call, args...)
   349  	//_, err := idbtx.Exec(call, args...)
   350  	if err != nil {
   351  		idbtx.Rollback()
   352  		db.iLog.Error(fmt.Sprintf("There is error to execute the Store procedure: %s with parameters %s with error: %s", procedureName, args, err.Error()))
   353  		return err
   354  	}
   355  
   356  	if blocaltx {
   357  		idbtx.Commit()
   358  	}
   359  
   360  	return nil
   361  }
   362  
   363  // ExeSPwithRow executes a stored procedure and returns the result set as a *sql.Rows object.
   364  // It takes the procedureName as a string and the args as variadic parameters.
   365  // The function measures the execution time and logs it using the PerformanceWithDuration method of the db.iLog object.
   366  // If there is an error during execution, it logs the error using the Error method of the db.iLog object.
   367  // The function handles panics and recovers from them, logging the error if any.
   368  // If a database transaction is not already in progress, it begins a new transaction and commits it at the end.
   369  // The function constructs the stored procedure call with placeholders for each parameter and the output parameter.
   370  // It checks if any of the parameters are output parameters and stores their names in the outputparameters slice.
   371  // The function uses the call string to call the stored procedure with the dynamic parameters and the output parameter.
   372  // It returns the result set as a *sql.Rows object and nil error if successful, otherwise it returns nil and the error.
   373  
   374  func (db *DBOperation) ExeSPwithRow(procedureName string, args ...interface{}) (*sql.Rows, error) {
   375  	startTime := time.Now()
   376  	defer func() {
   377  		elapsed := time.Since(startTime)
   378  		db.iLog.PerformanceWithDuration("dbconn.ExeSPwithRow", elapsed)
   379  	}()
   380  
   381  	defer func() {
   382  		if err := recover(); err != nil {
   383  			db.iLog.Error(fmt.Sprintf("There is error to execute store procedure %s in database with error: %s", procedureName, err))
   384  			return
   385  		}
   386  	}()
   387  
   388  	// Construct the stored procedure call with placeholders for each parameter and the output parameter
   389  	db.iLog.Debug(fmt.Sprintf("start execute the Store procedure to return rows: %s with parameters %s...", procedureName, args))
   390  
   391  	idbtx := db.DBTx
   392  	blocaltx := false
   393  
   394  	if idbtx == nil {
   395  		idbtx, err = DB.Begin()
   396  		blocaltx = true
   397  		if err != nil {
   398  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   399  			return nil, err
   400  		}
   401  		defer idbtx.Commit()
   402  	}
   403  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   404  	defer cancel()
   405  
   406  	var outputparameters []string
   407  	placeholders := make([]string, len(args))
   408  	for i := range args {
   409  		output, parameter := db.chechoutputparameter(args[i].(string))
   410  		if output {
   411  			outputparameters = append(outputparameters, parameter)
   412  		}
   413  		placeholders[i] = "?"
   414  	}
   415  	//placeholders = append(placeholders, "@output_param")
   416  	call := fmt.Sprintf("CALL %s(%s)", procedureName, strings.Join(placeholders, ","))
   417  
   418  	// Call the stored procedure with the dynamic parameters and the output parameter
   419  
   420  	rows, err := idbtx.QueryContext(ctx, call, args...)
   421  	//rows, err := idbtx.Query(call, args...)
   422  	defer rows.Close()
   423  
   424  	if err != nil {
   425  		idbtx.Rollback()
   426  		db.iLog.Error(fmt.Sprintf("There is error to execute the Store procedure: %s with parameters %s with error: %s", procedureName, args, err.Error()))
   427  		return nil, err
   428  	}
   429  
   430  	if blocaltx {
   431  		idbtx.Commit()
   432  	}
   433  
   434  	return rows, nil
   435  }
   436  
   437  // ExecSP_Json executes a stored procedure with the given name and arguments,
   438  // and returns the result as a slice of maps, where each map represents a row
   439  // of the result set. If an error occurs during execution, it returns nil and
   440  // the error.
   441  //
   442  // The execution time of the stored procedure is logged using the PerformanceWithDuration
   443  // method of the associated logger.
   444  //
   445  // If a panic occurs during execution, it is recovered and logged as an error.
   446  //
   447  // The execution of the stored procedure is logged using the Debug method of the
   448  // associated logger.
   449  //
   450  // The result set is obtained by calling the ExeSPwithRow method of the DBOperation
   451  // instance, and the rows are closed before returning.
   452  //
   453  // If an error occurs during execution, it is logged as an error and returned.
   454  //
   455  // The result set is converted to a JSON representation using the Conto_Json method
   456  // of the DBOperation instance, and returned.
   457  
   458  func (db *DBOperation) ExecSP_Json(procedureName string, args ...interface{}) ([]map[string]interface{}, error) {
   459  	startTime := time.Now()
   460  	defer func() {
   461  		elapsed := time.Since(startTime)
   462  		db.iLog.PerformanceWithDuration("dbconn.ExecSP_Json", elapsed)
   463  	}()
   464  	/*
   465  		defer func() {
   466  			if err := recover(); err != nil {
   467  				db.iLog.Error(fmt.Sprintf("There is error to execute store procedure %s in database with error: %s", procedureName, err))
   468  				return
   469  			}
   470  		}()
   471  	*/
   472  	db.iLog.Debug(fmt.Sprintf("start execute the Store procedure: %s with parameters %s...", procedureName, args))
   473  	rows, err := db.ExeSPwithRow(procedureName, args...)
   474  	defer rows.Close()
   475  	if err != nil {
   476  		db.iLog.Error(fmt.Sprintf("There is error to execute the Store procedure: %s with parameters %s with error: %s", procedureName, args, err.Error()))
   477  		return nil, err
   478  	}
   479  	return db.Conto_Json(rows)
   480  }
   481  
   482  // chechoutputparameter checks the output parameter of a given string.
   483  // It splits the string by space and determines if it contains the word "output".
   484  // If it does, it sets the output flag to true and returns the parameter without the word "output".
   485  // The function also logs debug messages for the start and result of the check.
   486  // It measures the performance duration of the function using the iLog.PerformanceWithDuration method.
   487  // If there is a panic during the execution, it logs an error message with the error details.
   488  // The function returns a boolean value indicating if the string contains an output parameter,
   489  // and the parameter itself without the word "output".
   490  
   491  func (db *DBOperation) chechoutputparameter(str string) (bool, string) {
   492  	startTime := time.Now()
   493  	defer func() {
   494  		elapsed := time.Since(startTime)
   495  		db.iLog.PerformanceWithDuration("dbconn.chechoutputparameter", elapsed)
   496  	}()
   497  	/*
   498  		defer func() {
   499  			if err := recover(); err != nil {
   500  				db.iLog.Error(fmt.Sprintf("There is error to chechoutputparameter with error: %s", err))
   501  				return
   502  			}
   503  		}()
   504  	*/
   505  	db.iLog.Debug(fmt.Sprintf("start to check the output parameter: %s...", str))
   506  	output := false
   507  	parameter := str
   508  	if strings.Contains(str, " output") {
   509  		parts := strings.Split(str, " ")
   510  		output = true
   511  		parameter = parts[0]
   512  
   513  	}
   514  	db.iLog.Debug(fmt.Sprintf("the output parameter: %s is %s...", parameter, output))
   515  	return output, parameter
   516  
   517  }
   518  
   519  // TableInsert inserts data into a specified table in the database.
   520  // It takes the table name, column names, and corresponding values as input.
   521  // It returns the last insert ID and any error encountered during the operation.
   522  // The function measures the performance duration of the operation using the PerformanceWithDuration method of the db.iLog object.
   523  func (db *DBOperation) TableInsert(TableName string, Columns []string, Values []string) (int64, error) {
   524  	startTime := time.Now()
   525  	defer func() {
   526  		elapsed := time.Since(startTime)
   527  		db.iLog.PerformanceWithDuration("dbconn.TableInsert", elapsed)
   528  	}()
   529  
   530  	defer func() {
   531  		if err := recover(); err != nil {
   532  			db.iLog.Error(fmt.Sprintf("There is error to execute table %s insert data with error: %s", TableName, err))
   533  			return
   534  		}
   535  	}()
   536  
   537  	db.iLog.Debug(fmt.Sprintf("start to insert the table: %s with columns: %s and values: %s...", TableName, Columns, Values))
   538  
   539  	idbtx := db.DBTx
   540  	blocaltx := false
   541  
   542  	if idbtx == nil {
   543  		idbtx, err = DB.Begin()
   544  		blocaltx = true
   545  		if err != nil {
   546  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   547  			return 0, err
   548  		}
   549  		defer idbtx.Commit()
   550  	}
   551  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   552  	defer cancel()
   553  
   554  	var querystr string
   555  
   556  	args := make([]interface{}, len(Values))
   557  	querystr = "INSERT INTO " + TableName + "(" + strings.Join(Columns, ",") + ") VALUES (" + strings.Repeat("?,", len(Columns)-1) + "?)"
   558  
   559  	for i, s := range Values {
   560  		args[i] = s
   561  	}
   562  
   563  	fmt.Println(querystr)
   564  	fmt.Println(args)
   565  	stmt, err := idbtx.PrepareContext(ctx, querystr)
   566  	//	stmt, err := idbtx.Prepare(querystr)
   567  	defer stmt.Close()
   568  	if err != nil {
   569  		idbtx.Rollback()
   570  		db.iLog.Error(fmt.Sprintf("There is error to prepare the insert statement with error: %s", err.Error()))
   571  		return 0, err
   572  	}
   573  	res, err := stmt.ExecContext(ctx, args...)
   574  	//res, err := stmt.Exec(args...)
   575  
   576  	if err != nil {
   577  		idbtx.Rollback()
   578  		db.iLog.Error(fmt.Sprintf("There is error to execute the insert statement with error: %s", err.Error()))
   579  		return 0, err
   580  	}
   581  	lastId, err := res.LastInsertId()
   582  	if err != nil {
   583  		idbtx.Rollback()
   584  		db.iLog.Error(fmt.Sprintf("There is error to get the last insert id with error: %s", err.Error()))
   585  	}
   586  
   587  	if blocaltx {
   588  		idbtx.Commit()
   589  	}
   590  
   591  	return lastId, err
   592  }
   593  
   594  // TableUpdate updates the specified table with the given columns, values, data types, and WHERE clause.
   595  // It returns the number of rows affected and any error encountered during the update operation.
   596  // The function measures the performance duration of the operation using the PerformanceWithDuration method of the db.iLog object.
   597  // If there is a panic during the execution, it logs an error message with the error details.
   598  func (db *DBOperation) TableUpdate(TableName string, Columns []string, Values []string, datatypes []int, Where string) (int64, error) {
   599  	startTime := time.Now()
   600  	defer func() {
   601  		elapsed := time.Since(startTime)
   602  		db.iLog.PerformanceWithDuration("dbconn.TableUpdate", elapsed)
   603  	}()
   604  
   605  	defer func() {
   606  		if err := recover(); err != nil {
   607  			db.iLog.Error(fmt.Sprintf("There is error to execute table %s update data with error: %s", TableName, err))
   608  			return
   609  		}
   610  	}()
   611  	db.iLog.Debug(fmt.Sprintf("start to update the table: %s with columns: %s and values: %s data type: %v", TableName, Columns, Values, datatypes))
   612  
   613  	//fmt.Println(WhereArgs)
   614  	//fmt.Println(Values)
   615  	var querystr string
   616  	var args []interface{}
   617  
   618  	idbtx := db.DBTx
   619  	blocaltx := false
   620  
   621  	if idbtx == nil {
   622  		idbtx, err = DB.Begin()
   623  		blocaltx = true
   624  		if err != nil {
   625  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   626  			return 0, err
   627  		}
   628  		defer idbtx.Commit()
   629  	}
   630  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   631  	defer cancel()
   632  
   633  	switch DatabaseType {
   634  	case "sqlserver":
   635  		setPlaceholders := make([]string, len(Columns))
   636  		for i, column := range Columns {
   637  
   638  			switch datatypes[i] {
   639  			case int(types.Integer):
   640  				setPlaceholders[i] = fmt.Sprintf("%s = %d", column, Values[i])
   641  			case int(types.Float):
   642  				setPlaceholders[i] = fmt.Sprintf("%s = %f", column, Values[i])
   643  
   644  			case int(types.Bool):
   645  				setPlaceholders[i] = fmt.Sprintf("%s = %t", column, Values[i])
   646  
   647  			default:
   648  				setPlaceholders[i] = fmt.Sprintf("%s = '%v'", column, Values[i])
   649  
   650  			}
   651  
   652  			//	setPlaceholders[i] = fmt.Sprintf("%s = '%s'", column, Values[i])
   653  		}
   654  		setClause := strings.Join(setPlaceholders, ", ")
   655  		querystr := fmt.Sprintf("UPDATE %s SET %s WHERE %s", TableName, setClause, Where)
   656  		args = []interface{}{}
   657  
   658  		db.iLog.Debug(fmt.Sprintf("The update query string is: %s  parametrs: %s...", querystr, args))
   659  
   660  		stmt, err := DB.PrepareContext(ctx, querystr)
   661  		//		stmt, err := DB.Prepare(querystr)
   662  		defer stmt.Close()
   663  		if err != nil {
   664  			db.iLog.Error(fmt.Sprintf("There is error to prepare the update statement with error: %s", err.Error()))
   665  			idbtx.Rollback()
   666  			return 0, err
   667  		}
   668  
   669  		res, err := stmt.ExecContext(ctx, args...)
   670  		//res, err := stmt.Exec(args...)
   671  		if err != nil {
   672  			db.iLog.Error(fmt.Sprintf("There is error to execute the update statement with error: %s", err.Error()))
   673  			idbtx.Rollback()
   674  			return 0, err
   675  		}
   676  
   677  		rowcount, err := res.RowsAffected()
   678  		if err != nil {
   679  			db.iLog.Error(fmt.Sprintf("There is error to get the affected rows with error: %s", err.Error()))
   680  			idbtx.Rollback()
   681  			return 0, err
   682  		}
   683  
   684  		if blocaltx {
   685  			idbtx.Commit()
   686  		}
   687  
   688  		return rowcount, err
   689  
   690  	default:
   691  		//	case "mysql":
   692  
   693  		querystr = "UPDATE " + TableName + " SET " + strings.Join(Columns, "=?,") + "=? WHERE " + Where
   694  
   695  		args := make([]interface{}, len(Values))
   696  
   697  		for i, s := range Values {
   698  			args[i] = s
   699  		}
   700  
   701  		//fmt.Println(querystr)
   702  		//fmt.Println(args)
   703  		db.iLog.Debug(fmt.Sprintf("The update query string is: %s  parametrs: %s...", querystr, args))
   704  
   705  		stmt, err := idbtx.PrepareContext(ctx, querystr)
   706  		//stmt, err := idbtx.Prepare(querystr)
   707  		defer stmt.Close()
   708  		if err != nil {
   709  			idbtx.Rollback()
   710  			db.iLog.Error(fmt.Sprintf("There is error to prepare the update statement with error: %s", err.Error()))
   711  			return 0, err
   712  		}
   713  		res, err := stmt.ExecContext(ctx, args...)
   714  		//res, err := stmt.Exec(args...)
   715  		if err != nil {
   716  			idbtx.Rollback()
   717  			db.iLog.Error(fmt.Sprintf("There is error to execute the update statement with error: %s", err.Error()))
   718  			return 0, err
   719  		}
   720  		rowcount, err := res.RowsAffected()
   721  
   722  		if blocaltx {
   723  			idbtx.Commit()
   724  		}
   725  
   726  		return rowcount, err
   727  	}
   728  
   729  }
   730  
   731  // TableDelete deletes records from a table based on the provided WHERE clause.
   732  // It returns the number of affected rows and an error, if any.
   733  // The function measures the performance duration of the operation using the PerformanceWithDuration method of the db.iLog object.
   734  func (db *DBOperation) TableDelete(TableName string, Where string) (int64, error) {
   735  	startTime := time.Now()
   736  	defer func() {
   737  		elapsed := time.Since(startTime)
   738  		db.iLog.PerformanceWithDuration("dbconn.TableDelete", elapsed)
   739  	}()
   740  
   741  	defer func() {
   742  		if err := recover(); err != nil {
   743  			db.iLog.Error(fmt.Sprintf("There is error to execute table %s delete with error: %s", TableName, err))
   744  			return
   745  		}
   746  	}()
   747  
   748  	db.iLog.Debug(fmt.Sprintf("Start to delete the table: %s with where: %s and whereargs: ", TableName, Where))
   749  
   750  	idbtx := db.DBTx
   751  	blocaltx := false
   752  
   753  	if idbtx == nil {
   754  		idbtx, err = DB.Begin()
   755  		blocaltx = true
   756  		if err != nil {
   757  			db.iLog.Error(fmt.Sprintf("There is error to begin database transaction with error: %s", err.Error()))
   758  			return 0, err
   759  		}
   760  		defer idbtx.Commit()
   761  	}
   762  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(com.DBTransactionTimeout))
   763  	defer cancel()
   764  
   765  	var querystr string
   766  	var args []interface{}
   767  	querystr = "DELETE FROM " + TableName + " WHERE " + Where
   768  
   769  	db.iLog.Debug(fmt.Sprintf("The delete query string is: %s  parametrs: %s...", querystr, args))
   770  
   771  	//fmt.Println(querystr)
   772  	//fmt.Println(args)
   773  	stmt, err := idbtx.PrepareContext(ctx, querystr)
   774  	//	stmt, err := idbtx.Prepare(querystr)
   775  	defer stmt.Close()
   776  	if err != nil {
   777  		idbtx.Rollback()
   778  		db.iLog.Error(fmt.Sprintf("There is error to prepare the delete statement with error: %s", err.Error()))
   779  		return 0, err
   780  	}
   781  	res, err := stmt.ExecContext(ctx, args...)
   782  	//	res, err := stmt.Exec(args...)
   783  	if err != nil {
   784  		idbtx.Rollback()
   785  		db.iLog.Error(fmt.Sprintf("There is error to execute the delete statement with error: %s", err.Error()))
   786  		return 0, err
   787  	}
   788  	lastId, err := res.RowsAffected()
   789  	if err != nil {
   790  		//idbtx.Commit()
   791  		db.iLog.Error(fmt.Sprintf("There is error to get the last insert id with error: %s", err.Error()))
   792  		//	return 0, err
   793  	}
   794  
   795  	if blocaltx {
   796  		idbtx.Commit()
   797  	}
   798  
   799  	return lastId, err
   800  }
   801  
   802  func (db *DBOperation) Conto_JsonbyList(rows *sql.Rows) (map[string][]interface{}, int, int, error) {
   803  	startTime := time.Now()
   804  	defer func() {
   805  		elapsed := time.Since(startTime)
   806  		db.iLog.PerformanceWithDuration("dbconn.Conto_JsonbyList", elapsed)
   807  	}()
   808  	/*
   809  		defer func() {
   810  			if err := recover(); err != nil {
   811  				db.iLog.Error(fmt.Sprintf("There is error to Conto_JsonbyList with error: %s", err))
   812  				return
   813  			}
   814  		}()
   815  	*/
   816  	db.iLog.Debug(fmt.Sprintf("Start to convert the rows to json...%s", rows))
   817  	cols, err := rows.ColumnTypes()
   818  	if err != nil {
   819  		db.iLog.Error(fmt.Sprintf("There is error to get the column types with error: %s", err.Error()))
   820  		return nil, 0, 0, err
   821  	}
   822  	data := make(map[string][]interface{})
   823  	colNames := make([]string, len(cols))
   824  	valuetmps := make([]interface{}, len(colNames))
   825  
   826  	ColumnNumbers := 0
   827  	for i, col := range cols {
   828  		colNames[i] = col.Name()
   829  		data[col.Name()] = []interface{}{}
   830  		ColumnNumbers = ColumnNumbers + 1
   831  	}
   832  
   833  	RowNumbers := 0
   834  	for rows.Next() {
   835  		values := make([]interface{}, len(colNames))
   836  		for i := range values {
   837  			//values[i] = new(interface{})
   838  			values[i] = &valuetmps[i]
   839  		}
   840  		err := rows.Scan(values...)
   841  		if err != nil {
   842  			db.iLog.Debug(fmt.Sprintf("There is error to scan the row with error: %s", err.Error()))
   843  			return nil, 0, 0, err
   844  
   845  		}
   846  		for i, name := range colNames {
   847  
   848  			var v interface{}
   849  
   850  			val := valuetmps[i]
   851  			b, ok := val.([]byte)
   852  			if ok {
   853  				v = string(b)
   854  			} else {
   855  				v = val
   856  			}
   857  			//data[name] = append(data[name], *(values[i].(*interface{})))
   858  			data[name] = append(data[name], v)
   859  		}
   860  		RowNumbers = RowNumbers + 1
   861  	}
   862  
   863  	if err := rows.Err(); err != nil {
   864  		db.iLog.Error(fmt.Sprintf("There is error to get the rows with error: %s", err.Error()))
   865  	}
   866  
   867  	db.iLog.Debug(fmt.Sprintf("The result of the conversion is: %s", data))
   868  
   869  	return data, ColumnNumbers, RowNumbers, nil
   870  
   871  }
   872  func (db *DBOperation) Conto_Json(rows *sql.Rows) ([]map[string]interface{}, error) {
   873  
   874  	startTime := time.Now()
   875  	defer func() {
   876  		elapsed := time.Since(startTime)
   877  		db.iLog.PerformanceWithDuration("dbconn.Conto_Json", elapsed)
   878  	}()
   879  	/*
   880  		defer func() {
   881  			if err := recover(); err != nil {
   882  				db.iLog.Error(fmt.Sprintf("There is error to Conto_Json with error: %s", err))
   883  				return
   884  			}
   885  		}()
   886  	*/
   887  	db.iLog.Debug(fmt.Sprintf("Start to convert the rows to json...%s", rows))
   888  
   889  	cols, err := rows.ColumnTypes()
   890  	if err != nil {
   891  		db.iLog.Error(fmt.Sprintf("There is error to get the column types with error: %s", err.Error()))
   892  		return nil, err
   893  
   894  	}
   895  
   896  	colNames := make([]string, len(cols))
   897  	for i, col := range cols {
   898  		colNames[i] = col.Name()
   899  	}
   900  	data := make([]map[string]interface{}, 0)
   901  	db.iLog.Debug(fmt.Sprintf("The column names are: %s", colNames))
   902  	//	db.iLog.Debug(fmt.Sprintf("rows : %s", rows))
   903  	valuetmps := make([]interface{}, len(colNames))
   904  
   905  	for rows.Next() {
   906  		row := make(map[string]interface{})
   907  		values := make([]interface{}, len(colNames))
   908  		for i := range values {
   909  
   910  			values[i] = &valuetmps[i]
   911  		}
   912  
   913  		err := rows.Scan(values...)
   914  		if err != nil {
   915  			db.iLog.Error(fmt.Sprintf("There is error to scan the row with error: %s", err.Error()))
   916  			return nil, err
   917  
   918  		}
   919  		//	db.iLog.Debug(fmt.Sprintf("The values of the row is: %s", values))
   920  		for i, name := range colNames {
   921  			var v interface{}
   922  			val := valuetmps[i]
   923  			b, ok := val.([]byte)
   924  			if ok {
   925  				v = string(b)
   926  			} else {
   927  				v = val
   928  			}
   929  			//	db.iLog.Debug(fmt.Sprintf("The row field %s is: %s", name, v))
   930  			row[name] = v
   931  			//row[name] = *(values[i].(*interface{}))
   932  			//	db.iLog.Debug(fmt.Sprintf("The row field %s is: %s", name, row[name]))
   933  		}
   934  		db.iLog.Debug(fmt.Sprintf("The row is: %s", row))
   935  		data = append(data, row)
   936  	}
   937  
   938  	if err := rows.Err(); err != nil {
   939  		db.iLog.Error(fmt.Sprintf("There is error to get the rows with error: %s", err.Error()))
   940  	}
   941  	db.iLog.Debug(fmt.Sprintf("The result of the conversion is: %s", data))
   942  	//jsondata, err := json.Marshal(data)
   943  	return data, nil
   944  }